CREATE DATABASE  IF NOT EXISTS `senbz_2011_0621` /*!40100 DEFAULT CHARACTER SET latin1 */;
USE `senbz_2011_0621`;
-- MySQL dump 10.13  Distrib 5.5.16, for Win32 (x86)
--
-- Host: localhost    Database: senbz_2011_0621
-- ------------------------------------------------------
-- Server version	5.5.24

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `blog_info`
--

DROP TABLE IF EXISTS `blog_info`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `blog_info` (
  `id` int(11) NOT NULL,
  `user_id` int(11) NOT NULL,
  `title` varchar(30) NOT NULL,
  `description` varchar(256) DEFAULT NULL,
  `notice` varchar(256) DEFAULT NULL,
  `theme` varchar(30) NOT NULL,
  `display_count` int(11) NOT NULL,
  `allow_comment` tinyint(1) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_id` (`user_id`),
  CONSTRAINT `user_id_refs_id_4d89682a` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `blog_info`
--

LOCK TABLES `blog_info` WRITE;
/*!40000 ALTER TABLE `blog_info` DISABLE KEYS */;
INSERT INTO `blog_info` VALUES (1,1,'千本桜','千本桜的博客','千本桜的博客公告','default',10,1);
/*!40000 ALTER TABLE `blog_info` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `auth_permission`
--

DROP TABLE IF EXISTS `auth_permission`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `auth_permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `content_type_id` int(11) NOT NULL,
  `codename` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `content_type_id` (`content_type_id`,`codename`),
  KEY `auth_permission_1bb8f392` (`content_type_id`),
  CONSTRAINT `content_type_id_refs_id_728de91f` FOREIGN KEY (`content_type_id`) REFERENCES `django_content_type` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=55 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `auth_permission`
--

LOCK TABLES `auth_permission` WRITE;
/*!40000 ALTER TABLE `auth_permission` DISABLE KEYS */;
INSERT INTO `auth_permission` VALUES (1,'Can add permission',1,'add_permission'),(2,'Can change permission',1,'change_permission'),(3,'Can delete permission',1,'delete_permission'),(4,'Can add group',2,'add_group'),(5,'Can change group',2,'change_group'),(6,'Can delete group',2,'delete_group'),(7,'Can add user',3,'add_user'),(8,'Can change user',3,'change_user'),(9,'Can delete user',3,'delete_user'),(10,'Can add message',4,'add_message'),(11,'Can change message',4,'change_message'),(12,'Can delete message',4,'delete_message'),(13,'Can add content type',5,'add_contenttype'),(14,'Can change content type',5,'change_contenttype'),(15,'Can delete content type',5,'delete_contenttype'),(16,'Can add session',6,'add_session'),(17,'Can change session',6,'change_session'),(18,'Can delete session',6,'delete_session'),(19,'Can add site',7,'add_site'),(20,'Can change site',7,'change_site'),(21,'Can delete site',7,'delete_site'),(22,'Can add comment',8,'add_comment'),(23,'Can change comment',8,'change_comment'),(24,'Can delete comment',8,'delete_comment'),(25,'Can add tag',9,'add_tag'),(26,'Can change tag',9,'change_tag'),(27,'Can delete tag',9,'delete_tag'),(28,'Can add tagged item',10,'add_taggeditem'),(29,'Can change tagged item',10,'change_taggeditem'),(30,'Can delete tagged item',10,'delete_taggeditem'),(31,'Can add task',11,'add_task'),(32,'Can change task',11,'change_task'),(33,'Can delete task',11,'delete_task'),(34,'Can add user profile',12,'add_userprofile'),(35,'Can change user profile',12,'change_userprofile'),(36,'Can delete user profile',12,'delete_userprofile'),(37,'Can add blog info',13,'add_bloginfo'),(38,'Can change blog info',13,'change_bloginfo'),(39,'Can delete blog info',13,'delete_bloginfo'),(40,'Can add category',14,'add_category'),(41,'Can change category',14,'change_category'),(42,'Can delete category',14,'delete_category'),(43,'Can add article',15,'add_article'),(44,'Can change article',15,'change_article'),(45,'Can delete article',15,'delete_article'),(46,'Can add link',16,'add_link'),(47,'Can change link',16,'change_link'),(48,'Can delete link',16,'delete_link'),(49,'Can add page',17,'add_page'),(50,'Can change page',17,'change_page'),(51,'Can delete page',17,'delete_page'),(52,'Can add history',18,'add_history'),(53,'Can change history',18,'change_history'),(54,'Can delete history',18,'delete_history');
/*!40000 ALTER TABLE `auth_permission` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `auth_group`
--

DROP TABLE IF EXISTS `auth_group`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `auth_group` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(80) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `auth_group`
--

LOCK TABLES `auth_group` WRITE;
/*!40000 ALTER TABLE `auth_group` DISABLE KEYS */;
/*!40000 ALTER TABLE `auth_group` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `auth_group_permissions`
--

DROP TABLE IF EXISTS `auth_group_permissions`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `auth_group_permissions` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `group_id` int(11) NOT NULL,
  `permission_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `group_id` (`group_id`,`permission_id`),
  KEY `auth_group_permissions_425ae3c4` (`group_id`),
  KEY `auth_group_permissions_1e014c8f` (`permission_id`),
  CONSTRAINT `group_id_refs_id_3cea63fe` FOREIGN KEY (`group_id`) REFERENCES `auth_group` (`id`),
  CONSTRAINT `permission_id_refs_id_5886d21f` FOREIGN KEY (`permission_id`) REFERENCES `auth_permission` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `auth_group_permissions`
--

LOCK TABLES `auth_group_permissions` WRITE;
/*!40000 ALTER TABLE `auth_group_permissions` DISABLE KEYS */;
/*!40000 ALTER TABLE `auth_group_permissions` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `blog_page`
--

DROP TABLE IF EXISTS `blog_page`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `blog_page` (
  `id` int(11) NOT NULL,
  `title` varchar(50) NOT NULL,
  `slug` varchar(50) NOT NULL,
  `blog_id` int(11) NOT NULL,
  `order` int(10) unsigned NOT NULL,
  `contents` longtext NOT NULL,
  `is_published` tinyint(1) NOT NULL,
  `allow_comment` tinyint(1) NOT NULL,
  `created_date` datetime NOT NULL,
  `updated_date` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `blog_page_56ae2a2a` (`slug`),
  KEY `blog_page_472bc96c` (`blog_id`),
  CONSTRAINT `blog_id_refs_id_f0394de` FOREIGN KEY (`blog_id`) REFERENCES `blog_info` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `blog_page`
--

LOCK TABLES `blog_page` WRITE;
/*!40000 ALTER TABLE `blog_page` DISABLE KEYS */;
INSERT INTO `blog_page` VALUES (1,'About Me','about',1,2,'<p><span style=\"color: #ff6600;\">Self introduction</span></p>\r\n<p>I was born in November 9th, 1985, at the town of Anqing, Anhui. My parents are doing business and farming. I have one elder brother. The family is in a well-to-do country with harmonized atmosphere.&nbsp;</p>\r\n<p><span style=\"color: #ff6600;\">Personal Information:</span></p>\r\n<p>Family Name: Yang &nbsp; Given Name: Wanjun<br />Date of Birth: Nov. 11, 1985 &nbsp; Birth Place: Beijing<br />Tel: &nbsp;13813825627<br />E-mail: ywjsailor@163.com</p>\r\n<p><span style=\"color: #ff6600;\">Objective</span></p>\r\n<p>To obtain a software developer position that allows me to learn new technologies while utilizing my experience developing web-based applications as well as my knowledge of ASP.Net, HTML, C#, and javascript.</p>\r\n<p><span style=\"color: #ff6600;\">Computer Skills</span></p>\r\n<p>Languages: ASP.NET, HTML, CSS, javascript, Translate-SQL, C#, Java, Python, Objective-c<br />Databases: MS SQL Server, My SQL, Oracle<br />Operating Systems: Windows, Linux, Android<br />Software: Visual Studio, Eclipse, Xcode, PowerDesigner, Apache, MS Office</p>\r\n<p><span style=\"color: #ff6600;\">Work Experience</span></p>\r\n<p>continue</p>\r\n<p>&nbsp;</p>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 137px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Languages: ASP, HTML, DHTML, XML, CSS, VBScript, javascript, PL/SQL, C, C++&nbsp;</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 137px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Databases: MS SQL Server 7.0, Sybase DMBS, MS-Access&nbsp;</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 137px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Operating Systems: Windows NT 4.0, Windows 98, UNIX&nbsp;</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 137px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Software: MS Visual Studio/InterDev 6.0, Windows NT 4.0 Option Pack/IIS, Real Media, Adobe Photoshop 5.0, Adobe Premier 5.0, MS Office 2000</div>',1,1,'2011-05-30 09:38:31','2011-05-30 10:59:29'),(2,'Contact Me','contact',1,3,'<p>Contact Me</p>\r\n<p>&nbsp;</p>\r\n<p>asssss</p>',1,1,'2011-05-31 10:32:51','2011-05-31 10:35:03'),(3,'黄金交易','glod-trade-history',1,3,'<p><strong>2012/3/9：</strong><br />买入：343.31*12(g) = 4119.72<br />卖出：345.98*12(g)&nbsp;= 4151.76&nbsp;<br />+ 32.04&nbsp;</p>',0,1,'2012-03-09 23:53:23','2012-03-09 23:57:19');
/*!40000 ALTER TABLE `blog_page` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `tags_taggeditem`
--

DROP TABLE IF EXISTS `tags_taggeditem`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tags_taggeditem` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `tag_id` int(11) NOT NULL,
  `content_type_id` int(11) NOT NULL,
  `object_pk` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `tag_id` (`tag_id`,`content_type_id`,`object_pk`),
  KEY `tags_taggeditem_3747b463` (`tag_id`),
  KEY `tags_taggeditem_1bb8f392` (`content_type_id`),
  KEY `tags_taggeditem_326712f1` (`object_pk`),
  CONSTRAINT `content_type_id_refs_id_d4bdce4` FOREIGN KEY (`content_type_id`) REFERENCES `django_content_type` (`id`),
  CONSTRAINT `tag_id_refs_id_34c70367` FOREIGN KEY (`tag_id`) REFERENCES `tags_tag` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=121 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `tags_taggeditem`
--

LOCK TABLES `tags_taggeditem` WRITE;
/*!40000 ALTER TABLE `tags_taggeditem` DISABLE KEYS */;
INSERT INTO `tags_taggeditem` VALUES (36,2,15,4),(50,2,15,5),(66,2,15,41),(67,2,15,42),(68,2,15,43),(69,2,15,44),(72,2,15,46),(74,2,15,47),(76,2,15,48),(86,2,15,56),(89,2,15,58),(91,2,15,59),(93,2,15,60),(97,2,15,62),(99,2,15,63),(101,2,15,65),(104,2,15,66),(105,2,15,67),(107,2,15,68),(109,2,15,69),(112,2,15,71),(114,2,15,72),(116,2,15,73),(118,2,15,74),(120,2,15,75),(41,3,15,2),(63,3,15,38),(35,5,15,4),(49,6,15,5),(30,6,15,6),(100,6,15,64),(44,7,15,9),(64,7,15,39),(79,7,15,51),(42,8,15,10),(25,8,15,17),(59,8,15,32),(27,10,15,8),(32,10,15,14),(43,10,15,15),(37,11,15,4),(31,11,15,6),(33,11,15,7),(24,11,15,16),(40,11,15,23),(88,11,15,57),(29,12,15,12),(38,12,15,22),(47,12,15,27),(82,12,15,53),(84,12,15,54),(28,13,15,13),(26,14,15,17),(34,15,15,19),(45,15,15,25),(46,15,15,26),(39,16,15,22),(52,16,15,27),(51,18,15,28),(53,18,15,29),(54,19,15,29),(55,19,15,31),(56,20,15,31),(57,21,15,31),(58,22,15,32),(60,23,15,33),(61,23,15,34),(62,23,15,35),(65,24,15,39),(70,25,15,45),(77,25,15,49),(78,25,15,50),(71,26,15,46),(73,26,15,47),(75,26,15,48),(80,27,15,51),(81,28,15,52),(83,29,15,53),(85,30,15,55),(92,30,15,60),(96,30,15,62),(98,30,15,63),(103,30,15,66),(106,30,15,68),(108,30,15,69),(110,30,15,70),(111,30,15,71),(113,30,15,72),(115,30,15,73),(117,30,15,74),(119,30,15,75),(87,31,15,56),(90,32,15,58),(94,32,15,60),(95,33,15,61),(102,34,15,65);
/*!40000 ALTER TABLE `tags_taggeditem` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `auth_user_user_permissions`
--

DROP TABLE IF EXISTS `auth_user_user_permissions`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `auth_user_user_permissions` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `permission_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_id` (`user_id`,`permission_id`),
  KEY `auth_user_user_permissions_403f60f` (`user_id`),
  KEY `auth_user_user_permissions_1e014c8f` (`permission_id`),
  CONSTRAINT `permission_id_refs_id_67e79cb` FOREIGN KEY (`permission_id`) REFERENCES `auth_permission` (`id`),
  CONSTRAINT `user_id_refs_id_dfbab7d` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `auth_user_user_permissions`
--

LOCK TABLES `auth_user_user_permissions` WRITE;
/*!40000 ALTER TABLE `auth_user_user_permissions` DISABLE KEYS */;
/*!40000 ALTER TABLE `auth_user_user_permissions` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `blog_link`
--

DROP TABLE IF EXISTS `blog_link`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `blog_link` (
  `id` int(11) NOT NULL,
  `link_url` varchar(200) NOT NULL,
  `title` varchar(30) NOT NULL,
  `description` varchar(50) DEFAULT NULL,
  `created_date` datetime NOT NULL,
  `blog_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `blog_link_472bc96c` (`blog_id`),
  CONSTRAINT `blog_id_refs_id_b244a1d` FOREIGN KEY (`blog_id`) REFERENCES `blog_info` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `blog_link`
--

LOCK TABLES `blog_link` WRITE;
/*!40000 ALTER TABLE `blog_link` DISABLE KEYS */;
INSERT INTO `blog_link` VALUES (1,'http://blog.csdn.net/pizzazhang/article/details/6284861','Android自带计算器研究','','2011-07-13 18:36:58',1);
/*!40000 ALTER TABLE `blog_link` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `blog_history`
--

DROP TABLE IF EXISTS `blog_history`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `blog_history` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `article_id` int(11) NOT NULL,
  `date` datetime NOT NULL,
  `ip_address` char(15) NOT NULL,
  `user_id` int(11) DEFAULT NULL,
  `session_id` varchar(50) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `blog_history_30525a19` (`article_id`),
  KEY `blog_history_403f60f` (`user_id`),
  CONSTRAINT `article_id_refs_id_502ad5ef` FOREIGN KEY (`article_id`) REFERENCES `blog_article` (`id`),
  CONSTRAINT `user_id_refs_id_560f92ef` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=335 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `blog_history`
--

LOCK TABLES `blog_history` WRITE;
/*!40000 ALTER TABLE `blog_history` DISABLE KEYS */;
INSERT INTO `blog_history` VALUES (34,21,'2011-07-06 23:06:02','127.0.0.1',1,'3978c8280b6fc7c78bdb0a3ad1f342c1'),(35,19,'2011-07-07 17:12:21','127.0.0.1',1,'d23b2597ada46384d9d640cb6eede4e0'),(36,20,'2011-07-07 17:13:46','127.0.0.1',1,'d23b2597ada46384d9d640cb6eede4e0'),(37,4,'2011-07-07 17:45:48','127.0.0.1',1,'d23b2597ada46384d9d640cb6eede4e0'),(38,12,'2011-07-07 19:25:37','127.0.0.1',1,'d23b2597ada46384d9d640cb6eede4e0'),(39,22,'2011-07-07 19:59:11','127.0.0.1',1,'d23b2597ada46384d9d640cb6eede4e0'),(40,22,'2011-07-07 20:01:41','127.0.1.1',NULL,'be4f5493f7b6f8e39da7c6e832e3c072'),(41,19,'2011-07-07 20:13:02','127.0.1.1',NULL,'c33e19b6f4f406d0bd3f65a4893361bf'),(42,19,'2011-07-07 20:14:14','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(43,6,'2011-07-07 20:15:23','127.0.1.1',NULL,'c0ff8aa88d51d0e7025e5179b8d0c10a'),(44,17,'2011-07-07 20:15:25','127.0.1.1',NULL,'c0ff8aa88d51d0e7025e5179b8d0c10a'),(45,22,'2011-07-07 20:15:35','127.0.1.1',NULL,'c0ff8aa88d51d0e7025e5179b8d0c10a'),(46,19,'2011-07-07 20:15:40','127.0.1.1',NULL,'c0ff8aa88d51d0e7025e5179b8d0c10a'),(47,20,'2011-07-07 20:15:52','127.0.1.1',NULL,'c0ff8aa88d51d0e7025e5179b8d0c10a'),(48,23,'2011-07-07 20:31:26','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(49,7,'2011-07-07 20:32:35','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(50,16,'2011-07-07 20:32:39','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(51,21,'2011-07-07 20:32:41','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(52,23,'2011-07-07 20:35:12','127.0.1.1',NULL,'c0ff8aa88d51d0e7025e5179b8d0c10a'),(53,19,'2011-07-08 13:11:56','10.20.52.113',NULL,'5f6d07d8fab235d547400d96638f05b9'),(54,20,'2011-07-08 13:13:04','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(55,24,'2011-07-08 14:14:20','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(56,20,'2011-07-08 14:21:11','10.20.52.113',NULL,'5f6d07d8fab235d547400d96638f05b9'),(57,24,'2011-07-08 14:58:49','10.20.52.35',NULL,'36f9e8ef7a19328a945e81213a318dc3'),(58,23,'2011-07-08 15:10:47','10.20.52.35',NULL,'36f9e8ef7a19328a945e81213a318dc3'),(59,24,'2011-07-08 15:25:47','10.20.52.110',NULL,'e173de74a8fe437651f5c6287dc2c14f'),(60,24,'2011-07-08 15:27:29','10.20.52.113',NULL,'5f6d07d8fab235d547400d96638f05b9'),(61,17,'2011-07-08 16:22:30','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(62,24,'2011-07-08 17:21:28','10.20.52.117',NULL,'f1eaef7ac633219278fa1fbbae992d27'),(63,20,'2011-07-08 17:24:51','10.20.52.117',NULL,'f1eaef7ac633219278fa1fbbae992d27'),(64,20,'2011-07-08 17:29:48','10.20.52.79',NULL,'1a2310c903c197f24b18e8accfde7e41'),(65,20,'2011-07-08 17:37:17','10.20.52.16',NULL,'58f3e4bcf3b2140a2cb04d3dd787766c'),(66,4,'2011-07-08 18:28:57','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(67,2,'2011-07-08 18:39:09','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(68,19,'2011-07-08 19:21:38','10.20.52.117',NULL,'f1eaef7ac633219278fa1fbbae992d27'),(69,22,'2011-07-08 20:07:59','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(70,10,'2011-07-08 20:08:21','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(71,6,'2011-07-08 20:08:25','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(72,8,'2011-07-08 20:10:35','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(73,14,'2011-07-08 20:10:41','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(74,15,'2011-07-08 20:10:44','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(75,24,'2011-07-11 09:03:40','10.20.52.16',NULL,'58f3e4bcf3b2140a2cb04d3dd787766c'),(76,22,'2011-07-11 10:02:44','10.20.52.117',NULL,'f1eaef7ac633219278fa1fbbae992d27'),(77,9,'2011-07-11 10:03:56','10.20.52.16',NULL,'58f3e4bcf3b2140a2cb04d3dd787766c'),(78,24,'2011-07-11 10:21:10','10.20.52.115',NULL,'e5504c72e4f5576039e9d5174da72170'),(79,17,'2011-07-11 10:33:19','10.20.52.16',NULL,'58f3e4bcf3b2140a2cb04d3dd787766c'),(80,22,'2011-07-11 10:33:59','10.20.52.16',NULL,'58f3e4bcf3b2140a2cb04d3dd787766c'),(81,23,'2011-07-11 10:34:08','10.20.52.16',NULL,'58f3e4bcf3b2140a2cb04d3dd787766c'),(82,21,'2011-07-11 10:34:18','10.20.52.16',NULL,'58f3e4bcf3b2140a2cb04d3dd787766c'),(83,19,'2011-07-11 10:34:35','10.20.52.16',NULL,'58f3e4bcf3b2140a2cb04d3dd787766c'),(84,2,'2011-07-11 10:37:13','10.20.52.16',NULL,'58f3e4bcf3b2140a2cb04d3dd787766c'),(85,13,'2011-07-11 10:37:24','10.20.52.16',NULL,'58f3e4bcf3b2140a2cb04d3dd787766c'),(86,14,'2011-07-11 10:37:45','10.20.52.16',NULL,'58f3e4bcf3b2140a2cb04d3dd787766c'),(87,15,'2011-07-11 10:39:34','10.20.52.16',NULL,'58f3e4bcf3b2140a2cb04d3dd787766c'),(88,24,'2011-07-11 11:41:35','10.20.52.79',NULL,'1a2310c903c197f24b18e8accfde7e41'),(89,24,'2011-07-11 13:55:04','10.20.52.112',NULL,'801058383da5280edbaaf3d2a8f4f191'),(90,19,'2011-07-11 14:09:30','10.20.52.79',NULL,'1a2310c903c197f24b18e8accfde7e41'),(91,22,'2011-07-11 14:37:40','10.20.52.79',NULL,'1a2310c903c197f24b18e8accfde7e41'),(92,23,'2011-07-11 14:38:03','10.20.52.79',NULL,'1a2310c903c197f24b18e8accfde7e41'),(93,17,'2011-07-11 14:38:34','10.20.52.79',NULL,'1a2310c903c197f24b18e8accfde7e41'),(94,20,'2011-07-11 14:40:31','10.20.52.96',NULL,'13787ee96f9983aea693213645674f13'),(95,24,'2011-07-11 14:40:36','10.20.52.96',NULL,'13787ee96f9983aea693213645674f13'),(96,19,'2011-07-11 14:41:55','10.20.52.96',NULL,'13787ee96f9983aea693213645674f13'),(97,24,'2011-07-11 17:12:39','10.20.52.124',NULL,'e8e4c944ead93add28ee4b42e4ef6dd4'),(98,19,'2011-07-11 17:20:39','10.20.52.124',NULL,'e8e4c944ead93add28ee4b42e4ef6dd4'),(99,24,'2011-07-11 17:29:28','10.20.52.123',NULL,'9bdedd8d3129c20fd559a4cca542051e'),(100,12,'2011-07-11 17:30:03','10.20.52.113',NULL,'5f6d07d8fab235d547400d96638f05b9'),(101,24,'2011-07-11 18:03:44','10.20.52.78',NULL,'a1524fdfc8daf63aedfc057587d79974'),(102,20,'2011-07-11 18:04:15','10.20.52.78',NULL,'a1524fdfc8daf63aedfc057587d79974'),(103,19,'2011-07-11 18:14:50','10.20.52.78',NULL,'a1524fdfc8daf63aedfc057587d79974'),(104,20,'2011-07-12 09:20:33','10.20.52.124',NULL,'e8e4c944ead93add28ee4b42e4ef6dd4'),(105,23,'2011-07-12 09:21:14','10.20.52.124',NULL,'e8e4c944ead93add28ee4b42e4ef6dd4'),(106,20,'2011-07-12 09:23:06','10.20.52.44',NULL,'afd10193ef2e5fb4260fc5903c9c3f4a'),(107,19,'2011-07-12 09:23:09','10.20.52.44',NULL,'afd10193ef2e5fb4260fc5903c9c3f4a'),(108,24,'2011-07-12 10:11:05','10.20.52.44',NULL,'afd10193ef2e5fb4260fc5903c9c3f4a'),(109,22,'2011-07-12 10:16:26','10.20.52.44',NULL,'afd10193ef2e5fb4260fc5903c9c3f4a'),(110,23,'2011-07-12 10:16:28','10.20.52.44',NULL,'afd10193ef2e5fb4260fc5903c9c3f4a'),(111,24,'2011-07-12 10:31:58','10.20.52.63',NULL,'59dc5b65816da452779abe3811858d8e'),(112,20,'2011-07-12 10:48:36','10.20.52.125',NULL,'1656cef514be5ba1c57d63fc3bf0390b'),(113,21,'2011-07-12 10:52:02','10.20.52.124',NULL,'e8e4c944ead93add28ee4b42e4ef6dd4'),(114,2,'2011-07-12 10:56:13','10.20.52.124',NULL,'e8e4c944ead93add28ee4b42e4ef6dd4'),(115,19,'2011-07-12 11:10:06','10.20.52.125',NULL,'1656cef514be5ba1c57d63fc3bf0390b'),(116,24,'2011-07-12 11:15:46','10.20.52.125',NULL,'1656cef514be5ba1c57d63fc3bf0390b'),(117,24,'2011-07-12 11:32:08','10.20.52.123',NULL,'cf692dede61f6e1e01133d828450ce78'),(118,20,'2011-07-12 11:33:14','10.20.52.123',NULL,'cf692dede61f6e1e01133d828450ce78'),(119,23,'2011-07-12 11:43:56','10.20.52.125',NULL,'1656cef514be5ba1c57d63fc3bf0390b'),(120,20,'2011-07-12 11:44:15','10.20.52.63',NULL,'ccd74b46ab3d94b031750716f720c1ca'),(121,25,'2011-07-12 11:50:05','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(122,19,'2011-07-12 12:34:19','10.20.52.123',NULL,'cf692dede61f6e1e01133d828450ce78'),(123,25,'2011-07-12 12:34:46','10.20.52.125',NULL,'1656cef514be5ba1c57d63fc3bf0390b'),(124,25,'2011-07-12 13:03:26','10.20.52.44',NULL,'afd10193ef2e5fb4260fc5903c9c3f4a'),(125,25,'2011-07-12 13:18:31','10.20.52.63',NULL,'ccd74b46ab3d94b031750716f720c1ca'),(126,24,'2011-07-12 13:21:34','10.20.52.63',NULL,'ccd74b46ab3d94b031750716f720c1ca'),(127,19,'2011-07-12 13:27:23','10.20.52.63',NULL,'ccd74b46ab3d94b031750716f720c1ca'),(128,17,'2011-07-12 13:35:02','10.20.52.125',NULL,'1656cef514be5ba1c57d63fc3bf0390b'),(129,25,'2011-07-12 13:46:26','10.20.52.78',NULL,'a1524fdfc8daf63aedfc057587d79974'),(130,20,'2011-07-12 16:23:27','10.20.52.126',NULL,'f8c198b5f3933dca749736b39a26ee1f'),(131,19,'2011-07-12 16:29:33','10.20.52.126',NULL,'f8c198b5f3933dca749736b39a26ee1f'),(132,24,'2011-07-12 16:29:37','10.20.52.126',NULL,'f8c198b5f3933dca749736b39a26ee1f'),(133,21,'2011-07-12 16:44:40','10.20.52.44',NULL,'afd10193ef2e5fb4260fc5903c9c3f4a'),(134,25,'2011-07-12 16:56:33','10.20.52.126',NULL,'f8c198b5f3933dca749736b39a26ee1f'),(135,21,'2011-07-12 16:56:34','10.20.52.126',NULL,'f8c198b5f3933dca749736b39a26ee1f'),(136,25,'2011-07-12 17:20:37','10.20.52.96',NULL,'13787ee96f9983aea693213645674f13'),(137,25,'2011-07-12 17:21:42','10.20.52.16',NULL,'58f3e4bcf3b2140a2cb04d3dd787766c'),(138,25,'2011-07-13 09:19:07','10.20.52.124',NULL,'e8e4c944ead93add28ee4b42e4ef6dd4'),(139,21,'2011-07-13 10:46:08','10.20.52.79',NULL,'1a2310c903c197f24b18e8accfde7e41'),(140,25,'2011-07-13 10:46:17','10.20.52.79',NULL,'1a2310c903c197f24b18e8accfde7e41'),(141,24,'2011-07-13 14:46:26','10.20.52.120',NULL,'671038e0d6b12293a1a7d45819349245'),(142,20,'2011-07-13 14:46:42','10.20.52.120',NULL,'671038e0d6b12293a1a7d45819349245'),(143,25,'2011-07-13 14:47:58','10.20.52.120',NULL,'671038e0d6b12293a1a7d45819349245'),(144,23,'2011-07-13 14:48:34','10.20.52.120',NULL,'671038e0d6b12293a1a7d45819349245'),(145,22,'2011-07-13 14:48:35','10.20.52.120',NULL,'671038e0d6b12293a1a7d45819349245'),(146,21,'2011-07-13 14:48:38','10.20.52.120',NULL,'671038e0d6b12293a1a7d45819349245'),(147,19,'2011-07-13 15:24:56','10.20.52.78',NULL,'322da7cf452a697da345e30e78cc7ea8'),(148,26,'2011-07-13 15:50:04','127.0.1.1',1,'6b534aefc7fc644a2f4168a20ab20db5'),(149,26,'2011-07-13 16:04:07','10.20.52.126',NULL,'f8c198b5f3933dca749736b39a26ee1f'),(150,23,'2011-07-13 16:04:10','10.20.52.126',NULL,'f8c198b5f3933dca749736b39a26ee1f'),(151,22,'2011-07-13 16:04:12','10.20.52.126',NULL,'f8c198b5f3933dca749736b39a26ee1f'),(152,26,'2011-07-13 17:43:43','10.20.52.124',NULL,'e8e4c944ead93add28ee4b42e4ef6dd4'),(153,26,'2011-07-14 09:26:57','10.20.52.117',NULL,'f1eaef7ac633219278fa1fbbae992d27'),(154,26,'2011-07-14 09:30:23','10.20.52.63',NULL,'ccd74b46ab3d94b031750716f720c1ca'),(155,26,'2011-07-14 10:37:23','10.20.52.125',NULL,'1656cef514be5ba1c57d63fc3bf0390b'),(156,26,'2011-07-14 13:04:17','10.20.52.123',NULL,'cf692dede61f6e1e01133d828450ce78'),(157,26,'2011-07-14 13:05:08','10.20.52.78',NULL,'322da7cf452a697da345e30e78cc7ea8'),(158,22,'2011-07-14 17:45:29','10.20.52.35',NULL,'36f9e8ef7a19328a945e81213a318dc3'),(159,25,'2011-07-14 17:48:33','10.20.52.101',NULL,'ac4270744d005382798a6dfd76ded2d9'),(160,24,'2011-07-14 17:48:40','10.20.52.101',NULL,'ac4270744d005382798a6dfd76ded2d9'),(161,23,'2011-07-14 17:48:45','10.20.52.101',NULL,'ac4270744d005382798a6dfd76ded2d9'),(162,22,'2011-07-14 17:48:46','10.20.52.101',NULL,'ac4270744d005382798a6dfd76ded2d9'),(163,19,'2011-07-14 17:48:59','10.20.52.101',NULL,'ac4270744d005382798a6dfd76ded2d9'),(164,20,'2011-07-14 17:49:03','10.20.52.101',NULL,'ac4270744d005382798a6dfd76ded2d9'),(165,4,'2011-07-14 17:49:39','10.20.52.101',NULL,'ac4270744d005382798a6dfd76ded2d9'),(166,26,'2011-07-14 17:50:25','10.20.52.101',NULL,'ac4270744d005382798a6dfd76ded2d9'),(167,22,'2011-08-04 10:06:57','127.0.0.1',NULL,'f09cdc78b771524faca2c73f52ff27f7'),(168,22,'2011-08-04 11:01:50','10.20.140.103',NULL,'73af90b57d565676eb8db4c5d2db33b0'),(169,22,'2011-08-04 11:02:38','10.20.140.103',1,'0155bea37005967e32e45f6aaeb420b5'),(170,27,'2011-08-04 11:26:20','10.20.140.103',1,'0155bea37005967e32e45f6aaeb420b5'),(171,24,'2011-08-04 11:39:49','10.20.140.103',1,'0155bea37005967e32e45f6aaeb420b5'),(172,26,'2011-08-04 14:46:11','10.20.140.99',NULL,'086de36b67a2efabbd016f778974389a'),(173,19,'2011-08-04 15:46:11','10.20.140.100',NULL,'a75e3ce6042fecfcfecda840d513c896'),(174,26,'2011-08-04 16:15:06','10.20.140.112',NULL,'c443cd7e3aeab07f767d502af24dd61d'),(175,20,'2011-08-04 16:18:38','10.20.140.70',NULL,'263c13a0ebddc8429e8858e196757db6'),(176,27,'2011-08-04 16:19:06','10.20.140.70',NULL,'263c13a0ebddc8429e8858e196757db6'),(177,19,'2011-08-04 16:20:30','10.20.140.70',NULL,'263c13a0ebddc8429e8858e196757db6'),(178,26,'2011-08-04 16:29:02','10.20.140.70',NULL,'263c13a0ebddc8429e8858e196757db6'),(179,24,'2011-08-04 16:36:34','10.20.140.70',NULL,'263c13a0ebddc8429e8858e196757db6'),(180,26,'2011-08-04 16:36:37','10.20.140.118',NULL,'d84a3178d84f97a74a5eaaabcb1d5130'),(181,25,'2011-08-04 17:24:00','10.20.140.70',NULL,'263c13a0ebddc8429e8858e196757db6'),(182,17,'2011-08-05 09:40:20','10.20.140.89',NULL,'74f6a3f1f027413eca081a9f10f2baf7'),(183,24,'2011-08-05 11:16:41','10.20.140.113',NULL,'94a9570850f53ddab38a22e90f732a17'),(184,26,'2011-08-05 11:33:41','10.20.140.113',NULL,'94a9570850f53ddab38a22e90f732a17'),(185,5,'2011-08-05 14:30:01','10.20.140.103',1,'0155bea37005967e32e45f6aaeb420b5'),(186,8,'2011-08-05 14:31:19','10.20.140.103',1,'0155bea37005967e32e45f6aaeb420b5'),(187,19,'2011-08-08 09:05:23','10.20.140.99',NULL,'086de36b67a2efabbd016f778974389a'),(188,19,'2011-08-08 09:47:44','10.20.140.98',NULL,'ef7e639bdcd0a29eb66f7c824cd7155a'),(189,24,'2011-08-08 16:01:19','10.20.140.118',NULL,'d84a3178d84f97a74a5eaaabcb1d5130'),(190,25,'2011-08-08 16:01:23','10.20.140.118',NULL,'d84a3178d84f97a74a5eaaabcb1d5130'),(191,16,'2011-08-08 16:01:52','10.20.140.118',NULL,'d84a3178d84f97a74a5eaaabcb1d5130'),(192,23,'2011-08-08 16:01:57','10.20.140.118',NULL,'d84a3178d84f97a74a5eaaabcb1d5130'),(193,19,'2011-08-08 16:02:06','10.20.140.118',NULL,'d84a3178d84f97a74a5eaaabcb1d5130'),(194,24,'2011-08-08 16:02:50','10.20.140.101',NULL,'058a5ebc256d860df429d133b191b79d'),(195,20,'2011-08-08 16:03:20','10.20.140.101',NULL,'058a5ebc256d860df429d133b191b79d'),(196,20,'2011-08-09 08:59:29','10.20.140.104',NULL,'9e9621625f5e74fd1eb822668b8b492d'),(197,24,'2011-08-09 08:59:35','10.20.140.104',NULL,'9e9621625f5e74fd1eb822668b8b492d'),(198,27,'2011-08-09 08:59:36','10.20.140.104',NULL,'9e9621625f5e74fd1eb822668b8b492d'),(199,26,'2011-08-09 08:59:41','10.20.140.104',NULL,'9e9621625f5e74fd1eb822668b8b492d'),(200,2,'2011-08-09 09:00:04','10.20.140.104',NULL,'9e9621625f5e74fd1eb822668b8b492d'),(201,15,'2011-08-09 09:00:27','10.20.140.104',NULL,'9e9621625f5e74fd1eb822668b8b492d'),(202,13,'2011-08-09 09:02:30','10.20.140.104',NULL,'9e9621625f5e74fd1eb822668b8b492d'),(203,9,'2011-08-09 09:02:53','10.20.140.104',NULL,'9e9621625f5e74fd1eb822668b8b492d'),(204,25,'2011-08-09 09:25:11','10.20.140.101',NULL,'058a5ebc256d860df429d133b191b79d'),(205,20,'2011-08-09 09:37:40','10.20.140.89',NULL,'74f6a3f1f027413eca081a9f10f2baf7'),(206,26,'2011-08-09 09:37:46','10.20.140.89',NULL,'74f6a3f1f027413eca081a9f10f2baf7'),(207,20,'2011-08-09 09:41:43','10.20.140.27',NULL,'a909e1e9f762ca868d90c6c8448d0ce7'),(208,26,'2011-08-09 09:41:50','10.20.140.27',NULL,'a909e1e9f762ca868d90c6c8448d0ce7'),(209,24,'2011-08-09 09:45:16','10.20.140.27',NULL,'a909e1e9f762ca868d90c6c8448d0ce7'),(210,20,'2011-08-09 11:13:17','10.20.140.151',NULL,'e194ca42fbbb9caaffc6e6e288bad4ce'),(211,19,'2011-08-09 11:13:23','10.20.140.151',NULL,'e194ca42fbbb9caaffc6e6e288bad4ce'),(212,20,'2011-08-09 11:22:04','10.20.140.103',1,'0155bea37005967e32e45f6aaeb420b5'),(213,19,'2011-08-09 13:00:16','10.20.140.101',NULL,'058a5ebc256d860df429d133b191b79d'),(214,19,'2011-08-09 14:36:15','10.20.140.70',NULL,'33aea72e21c2904636873145a88c0450'),(215,26,'2011-08-09 14:40:08','10.20.140.70',NULL,'33aea72e21c2904636873145a88c0450'),(216,24,'2011-08-09 17:20:43','10.20.140.99',NULL,'086de36b67a2efabbd016f778974389a'),(217,25,'2011-08-09 17:21:04','10.20.140.99',NULL,'086de36b67a2efabbd016f778974389a'),(218,27,'2011-08-09 17:21:06','10.20.140.99',NULL,'086de36b67a2efabbd016f778974389a'),(219,23,'2011-08-09 17:21:11','10.20.140.99',NULL,'086de36b67a2efabbd016f778974389a'),(220,20,'2011-08-09 17:21:14','10.20.140.99',NULL,'086de36b67a2efabbd016f778974389a'),(221,24,'2011-08-11 15:22:01','10.20.52.100',NULL,'29608d23d6ca1561fe0817256f04c9e0'),(222,19,'2011-08-11 15:22:07','10.20.52.100',NULL,'29608d23d6ca1561fe0817256f04c9e0'),(223,20,'2011-08-11 15:22:45','10.20.52.100',1,'40cfd34d4c41e2c3e37f66ced3053b53'),(224,19,'2011-08-11 15:23:27','10.20.52.100',1,'40cfd34d4c41e2c3e37f66ced3053b53'),(225,28,'2011-08-11 16:09:25','10.20.52.100',1,'40cfd34d4c41e2c3e37f66ced3053b53'),(226,27,'2011-08-17 11:31:26','10.20.52.100',1,'40cfd34d4c41e2c3e37f66ced3053b53'),(227,29,'2011-08-17 11:42:53','10.20.52.100',1,'40cfd34d4c41e2c3e37f66ced3053b53'),(228,24,'2011-08-17 16:13:15','10.20.52.100',1,'40cfd34d4c41e2c3e37f66ced3053b53'),(229,29,'2011-08-17 16:51:26','127.0.0.1',NULL,'5b1e458a952f1b1118e1df371abcb43b'),(230,24,'2011-08-17 16:53:48','127.0.0.1',NULL,'5b1e458a952f1b1118e1df371abcb43b'),(233,28,'2011-08-18 10:35:56','10.20.52.146',NULL,'5e89569e85687f99e01f1f5262b0dbc6'),(234,29,'2011-08-18 10:36:26','10.20.52.146',NULL,'5e89569e85687f99e01f1f5262b0dbc6'),(235,31,'2011-08-18 10:42:21','10.20.52.100',1,'40cfd34d4c41e2c3e37f66ced3053b53'),(236,5,'2011-08-18 15:26:38','10.20.52.100',1,'40cfd34d4c41e2c3e37f66ced3053b53'),(237,17,'2011-08-18 16:16:34','10.20.52.100',1,'40cfd34d4c41e2c3e37f66ced3053b53'),(238,32,'2011-08-18 16:20:02','10.20.52.100',1,'40cfd34d4c41e2c3e37f66ced3053b53'),(239,32,'2011-08-18 17:32:01','10.20.52.100',NULL,'1e20a5f0339aa73dc29cf6ecf0501a01'),(240,31,'2011-08-18 17:32:12','10.20.52.100',NULL,'1e20a5f0339aa73dc29cf6ecf0501a01'),(241,21,'2011-08-18 17:52:58','10.20.52.100',1,'40cfd34d4c41e2c3e37f66ced3053b53'),(242,23,'2011-08-18 17:53:25','10.20.52.100',1,'40cfd34d4c41e2c3e37f66ced3053b53'),(243,18,'2011-08-18 19:09:49','10.20.52.100',1,'40cfd34d4c41e2c3e37f66ced3053b53'),(244,29,'2011-08-19 13:33:41','10.20.52.100',NULL,'6a49c3da8dbdf997d409bdc4d7a9e17c'),(247,26,'2011-08-19 14:08:48','10.20.52.115',NULL,'8a1a002935615dd42ec3a42f57461fd9'),(248,25,'2011-08-19 14:10:14','10.20.52.115',NULL,'8a1a002935615dd42ec3a42f57461fd9'),(249,34,'2011-08-19 14:54:02','10.20.52.100',1,'40cfd34d4c41e2c3e37f66ced3053b53'),(250,24,'2011-08-23 09:13:29','10.20.52.115',NULL,'8a1a002935615dd42ec3a42f57461fd9'),(251,19,'2011-08-23 09:13:39','10.20.52.115',NULL,'8a1a002935615dd42ec3a42f57461fd9'),(252,20,'2011-08-23 11:41:17','10.20.52.115',NULL,'8a1a002935615dd42ec3a42f57461fd9'),(253,2,'2011-08-23 11:41:51','10.20.52.115',NULL,'8a1a002935615dd42ec3a42f57461fd9'),(254,23,'2011-08-23 11:42:17','10.20.52.115',NULL,'8a1a002935615dd42ec3a42f57461fd9'),(255,16,'2011-08-23 11:42:29','10.20.52.115',NULL,'8a1a002935615dd42ec3a42f57461fd9'),(256,29,'2011-08-23 11:42:47','10.20.52.115',NULL,'8a1a002935615dd42ec3a42f57461fd9'),(257,2,'2011-08-26 13:43:32','10.20.52.100',1,'40cfd34d4c41e2c3e37f66ced3053b53'),(259,24,'2011-08-27 19:12:03','127.0.0.1',1,'5f72a330ccf29e956443c645350dee89'),(260,5,'2011-08-27 19:12:49','127.0.0.1',1,'5f72a330ccf29e956443c645350dee89'),(261,32,'2011-08-27 21:21:41','127.0.0.1',1,'5f72a330ccf29e956443c645350dee89'),(262,33,'2011-08-29 16:54:18','10.20.52.100',1,'40cfd34d4c41e2c3e37f66ced3053b53'),(263,34,'2011-08-29 20:02:33','180.110.179.30',NULL,'89ff32eb806aaa3abf6bacd0c4ae9b8a'),(264,34,'2011-08-29 20:02:52','180.110.179.30',1,'08603fd1ccfadae37791c0d808594409'),(265,33,'2011-08-29 20:37:32','180.110.179.30',1,'08603fd1ccfadae37791c0d808594409'),(266,35,'2011-08-29 23:01:30','180.110.179.30',1,'08603fd1ccfadae37791c0d808594409'),(267,36,'2011-09-01 23:00:04','192.168.56.1',1,'08603fd1ccfadae37791c0d808594409'),(268,14,'2011-10-29 23:28:10','192.168.56.1',1,'f995e191589705ffc17de4a6bc1aa7e0'),(269,37,'2011-10-29 23:43:47','192.168.56.1',1,'f995e191589705ffc17de4a6bc1aa7e0'),(270,37,'2011-11-03 00:56:49','192.168.56.1',NULL,'b3b7340ee4766d628a9d171aba342341'),(271,33,'2011-11-03 00:56:57','192.168.56.1',NULL,'b3b7340ee4766d628a9d171aba342341'),(272,2,'2011-11-03 17:17:19','192.168.1.5',NULL,'b3b7340ee4766d628a9d171aba342341'),(273,2,'2011-11-04 23:51:03','169.254.109.65',1,'70001a247d6075a94f0473801b353176'),(274,38,'2011-11-06 00:44:20','169.254.109.65',1,'70001a247d6075a94f0473801b353176'),(275,9,'2011-11-06 18:59:41','169.254.109.65',1,'70001a247d6075a94f0473801b353176'),(276,39,'2011-11-06 20:11:52','169.254.109.65',1,'70001a247d6075a94f0473801b353176'),(277,40,'2011-11-06 20:50:51','169.254.109.65',1,'70001a247d6075a94f0473801b353176'),(278,41,'2011-11-09 22:46:40','169.254.109.65',1,'70001a247d6075a94f0473801b353176'),(279,27,'2011-11-09 23:05:11','169.254.109.65',1,'70001a247d6075a94f0473801b353176'),(280,42,'2011-12-25 00:20:06','169.254.109.65',1,'d5632fbe1e5d32a34d55cafc6e153589'),(281,43,'2011-12-27 22:43:28','169.254.109.65',1,'d5632fbe1e5d32a34d55cafc6e153589'),(282,44,'2011-12-28 21:34:06','169.254.109.65',1,'d5632fbe1e5d32a34d55cafc6e153589'),(283,45,'2011-12-30 13:18:30','169.254.109.65',1,'d5632fbe1e5d32a34d55cafc6e153589'),(284,46,'2012-01-01 01:27:51','169.254.109.65',1,'d5632fbe1e5d32a34d55cafc6e153589'),(285,47,'2012-01-01 02:36:02','169.254.109.65',1,'d5632fbe1e5d32a34d55cafc6e153589'),(286,48,'2012-01-01 20:51:33','169.254.109.65',1,'d5632fbe1e5d32a34d55cafc6e153589'),(287,49,'2012-01-02 18:07:04','169.254.109.65',1,'d5632fbe1e5d32a34d55cafc6e153589'),(288,24,'2012-01-05 23:24:35','169.254.109.65',1,'d5632fbe1e5d32a34d55cafc6e153589'),(289,50,'2012-01-05 23:35:05','169.254.109.65',1,'d5632fbe1e5d32a34d55cafc6e153589'),(290,51,'2012-01-11 23:38:36','169.254.109.65',1,'d5632fbe1e5d32a34d55cafc6e153589'),(291,52,'2012-01-14 00:50:27','169.254.109.65',1,'d5632fbe1e5d32a34d55cafc6e153589'),(292,53,'2012-02-12 22:28:24','192.168.200.74',1,'d5632fbe1e5d32a34d55cafc6e153589'),(293,22,'2012-04-29 11:28:41','127.0.0.1',1,'0d0df72ed1aed5a18b24a4cd0df9b70c'),(294,5,'2012-04-29 11:30:11','127.0.0.1',1,'0d0df72ed1aed5a18b24a4cd0df9b70c'),(295,24,'2012-04-29 11:47:29','127.0.0.1',1,'0d0df72ed1aed5a18b24a4cd0df9b70c'),(296,54,'2012-04-29 11:47:53','127.0.0.1',1,'0d0df72ed1aed5a18b24a4cd0df9b70c'),(297,12,'2012-04-29 11:48:59','127.0.0.1',1,'0d0df72ed1aed5a18b24a4cd0df9b70c'),(298,55,'2012-04-29 12:04:05','127.0.0.1',1,'0d0df72ed1aed5a18b24a4cd0df9b70c'),(299,56,'2012-04-29 13:25:54','127.0.0.1',1,'0d0df72ed1aed5a18b24a4cd0df9b70c'),(300,27,'2012-04-29 13:40:46','127.0.0.1',1,'0d0df72ed1aed5a18b24a4cd0df9b70c'),(301,57,'2012-04-29 15:34:10','127.0.0.1',1,'0d0df72ed1aed5a18b24a4cd0df9b70c'),(302,58,'2012-05-05 20:12:36','127.0.0.1',1,'0d0df72ed1aed5a18b24a4cd0df9b70c'),(303,59,'2012-05-05 20:56:49','127.0.0.1',1,'0d0df72ed1aed5a18b24a4cd0df9b70c'),(304,60,'2012-05-05 23:47:35','127.0.0.1',1,'0d0df72ed1aed5a18b24a4cd0df9b70c'),(305,61,'2012-05-06 12:29:49','127.0.0.1',1,'0d0df72ed1aed5a18b24a4cd0df9b70c'),(306,62,'2012-05-06 16:45:04','127.0.0.1',1,'0d0df72ed1aed5a18b24a4cd0df9b70c'),(307,63,'2012-05-06 17:35:52','127.0.0.1',1,'0d0df72ed1aed5a18b24a4cd0df9b70c'),(308,20,'2012-05-06 21:45:03','127.0.0.1',1,'0d0df72ed1aed5a18b24a4cd0df9b70c'),(309,16,'2012-05-06 21:45:11','127.0.0.1',1,'0d0df72ed1aed5a18b24a4cd0df9b70c'),(310,6,'2012-05-13 16:20:35','192.168.1.102',1,'45b17d1dd029feb16270af39f653a386'),(311,64,'2012-05-13 16:30:20','192.168.1.102',1,'45b17d1dd029feb16270af39f653a386'),(312,65,'2012-05-13 17:13:11','192.168.1.102',1,'45b17d1dd029feb16270af39f653a386'),(313,66,'2012-05-13 19:09:44','192.168.1.102',1,'45b17d1dd029feb16270af39f653a386'),(314,67,'2012-05-13 22:33:50','192.168.1.102',1,'45b17d1dd029feb16270af39f653a386'),(315,60,'2012-05-19 22:08:43','192.168.56.1',1,'45b17d1dd029feb16270af39f653a386'),(316,68,'2012-05-19 23:48:17','192.168.56.1',1,'45b17d1dd029feb16270af39f653a386'),(317,18,'2012-05-20 00:31:10','192.168.56.1',1,'45b17d1dd029feb16270af39f653a386'),(318,69,'2012-05-20 12:42:21','192.168.56.1',1,'45b17d1dd029feb16270af39f653a386'),(319,70,'2012-05-20 13:25:46','192.168.56.1',1,'45b17d1dd029feb16270af39f653a386'),(320,71,'2012-05-25 23:10:01','192.168.56.1',1,'45b17d1dd029feb16270af39f653a386'),(321,72,'2012-06-02 11:44:52','192.168.56.1',1,'45b17d1dd029feb16270af39f653a386'),(322,73,'2012-06-10 12:49:00','192.168.56.1',1,'45b17d1dd029feb16270af39f653a386'),(323,24,'2012-06-14 23:48:05','192.168.1.103',1,'1f6d77fafec030001078b3b5c40f4428'),(324,19,'2012-06-14 23:51:13','192.168.1.103',1,'1f6d77fafec030001078b3b5c40f4428'),(325,20,'2012-06-14 23:51:18','192.168.1.103',1,'1f6d77fafec030001078b3b5c40f4428'),(326,24,'2012-06-14 23:51:51','192.168.1.108',NULL,'2e70e3c7042b50e7ac7fe13a5fc18c47'),(327,74,'2012-06-23 18:27:19','192.168.56.1',1,'45b17d1dd029feb16270af39f653a386'),(328,75,'2012-07-01 12:13:51','192.168.56.1',1,'45b17d1dd029feb16270af39f653a386'),(329,63,'2012-07-15 17:11:43','192.168.56.1',1,'45b17d1dd029feb16270af39f653a386'),(330,62,'2012-07-22 10:08:31','169.254.204.117',1,'45b17d1dd029feb16270af39f653a386'),(331,41,'2012-08-25 00:48:58','169.254.204.117',NULL,'881ff8278e21d168c0cd5a6d8e666a52'),(332,27,'2012-08-25 00:49:00','169.254.204.117',NULL,'881ff8278e21d168c0cd5a6d8e666a52'),(333,55,'2012-08-25 01:03:29','169.254.204.117',NULL,'881ff8278e21d168c0cd5a6d8e666a52'),(334,54,'2012-08-25 02:53:38','169.254.204.117',NULL,'881ff8278e21d168c0cd5a6d8e666a52');
/*!40000 ALTER TABLE `blog_history` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `django_site`
--

DROP TABLE IF EXISTS `django_site`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `django_site` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `domain` varchar(100) NOT NULL,
  `name` varchar(50) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `django_site`
--

LOCK TABLES `django_site` WRITE;
/*!40000 ALTER TABLE `django_site` DISABLE KEYS */;
INSERT INTO `django_site` VALUES (1,'example.com','example.com');
/*!40000 ALTER TABLE `django_site` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `auth_user`
--

DROP TABLE IF EXISTS `auth_user`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `auth_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(30) NOT NULL,
  `first_name` varchar(30) NOT NULL,
  `last_name` varchar(30) NOT NULL,
  `email` varchar(75) NOT NULL,
  `password` varchar(128) NOT NULL,
  `is_staff` tinyint(1) NOT NULL,
  `is_active` tinyint(1) NOT NULL,
  `is_superuser` tinyint(1) NOT NULL,
  `last_login` datetime NOT NULL,
  `date_joined` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `auth_user`
--

LOCK TABLES `auth_user` WRITE;
/*!40000 ALTER TABLE `auth_user` DISABLE KEYS */;
INSERT INTO `auth_user` VALUES (1,'ywjsailor','','','ywjsailor@163.com','pbkdf2_sha256$10000$eGPS8bALgnG4$UaJycnZ6kQdINkW9X40VhTopGaenakR27Gxl7B8XQLA=',1,1,1,'2012-06-14 23:46:27','2011-06-21 11:23:41');
/*!40000 ALTER TABLE `auth_user` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `blog_category`
--

DROP TABLE IF EXISTS `blog_category`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `blog_category` (
  `id` int(11) NOT NULL,
  `title` varchar(50) NOT NULL,
  `slug` varchar(50) NOT NULL,
  `description` varchar(256) DEFAULT NULL,
  `type` int(11) NOT NULL,
  `order` int(10) unsigned NOT NULL,
  `parent_id` int(11) DEFAULT NULL,
  `blog_id` int(11) NOT NULL,
  `created_date` datetime NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `blog_id` (`blog_id`,`slug`),
  KEY `blog_category_56ae2a2a` (`slug`),
  KEY `blog_category_63f17a16` (`parent_id`),
  KEY `blog_category_472bc96c` (`blog_id`),
  CONSTRAINT `blog_id_refs_id_45a5031` FOREIGN KEY (`blog_id`) REFERENCES `blog_info` (`id`),
  CONSTRAINT `parent_id_refs_id_499df47` FOREIGN KEY (`parent_id`) REFERENCES `blog_category` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `blog_category`
--

LOCK TABLES `blog_category` WRITE;
/*!40000 ALTER TABLE `blog_category` DISABLE KEYS */;
INSERT INTO `blog_category` VALUES (1,'默认分类','default','默认分类',1,18,NULL,1,'2011-05-15 15:20:45'),(2,'Python','python','一种面向对象,直译式计算机程序设计语言',1,5,NULL,1,'2011-05-15 15:34:40'),(3,'Django','django','python的一种Web开发框架',1,6,2,1,'2011-05-15 15:49:44'),(4,'游戏','game','PC游戏，PS游戏等攻略',1,13,NULL,1,'2011-05-20 12:33:29'),(5,'数据库','database','',1,14,NULL,1,'2011-05-21 20:03:27'),(6,'Ubuntu','ubuntu','',1,8,NULL,1,'2011-05-21 21:25:19'),(7,'Dot.NET','dotnet','',1,1,NULL,1,'2011-05-26 10:16:14'),(8,'Javascript','javascript','',1,10,11,1,'2011-05-26 10:16:14'),(9,'CSS','css','',1,12,11,1,'2011-05-26 10:16:14'),(10,'OpenLayers','openlayer','GIS',1,7,NULL,1,'2011-06-02 10:47:44'),(11,'Web前端','webfront','Web的前端开发技术，如Javascript, CSS',1,9,NULL,1,'2011-06-15 17:26:53'),(12,'SQL Server','sqlserver','',1,15,5,1,'2011-06-17 19:46:38'),(13,'MySQL','mysql','',1,16,5,1,'2011-06-17 19:46:38'),(14,'Oracle','oracle','',1,17,5,1,'2011-06-17 19:46:38'),(15,'Android','android','',1,3,NULL,1,'2011-07-06 22:04:33'),(16,'Java','java','',1,2,NULL,1,'2011-07-06 22:04:33'),(17,'iOS','ios','iPhone, iPad开发',1,4,NULL,1,'2011-08-11 15:21:22'),(18,'ExtJS','extjs','',1,11,11,1,'2011-08-29 13:10:24'),(19,'PyQT','pyqt','',1,19,2,1,'2012-04-29 11:26:17');
/*!40000 ALTER TABLE `blog_category` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `django_content_type`
--

DROP TABLE IF EXISTS `django_content_type`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `django_content_type` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `app_label` varchar(100) NOT NULL,
  `model` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `app_label` (`app_label`,`model`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `django_content_type`
--

LOCK TABLES `django_content_type` WRITE;
/*!40000 ALTER TABLE `django_content_type` DISABLE KEYS */;
INSERT INTO `django_content_type` VALUES (1,'permission','auth','permission'),(2,'group','auth','group'),(3,'user','auth','user'),(4,'message','auth','message'),(5,'content type','contenttypes','contenttype'),(6,'session','sessions','session'),(7,'site','sites','site'),(8,'comment','comments','comment'),(9,'tag','tags','tag'),(10,'tagged item','tags','taggeditem'),(11,'task','index','task'),(12,'user profile','blog','userprofile'),(13,'blog info','blog','bloginfo'),(14,'category','blog','category'),(15,'article','blog','article'),(16,'link','blog','link'),(17,'page','blog','page'),(18,'history','blog','history');
/*!40000 ALTER TABLE `django_content_type` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `index_task`
--

DROP TABLE IF EXISTS `index_task`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `index_task` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `content_type_id` int(11) NOT NULL,
  `object_pk` int(10) unsigned NOT NULL,
  `status` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `content_type_id` (`content_type_id`,`object_pk`),
  KEY `index_task_1bb8f392` (`content_type_id`),
  KEY `index_task_326712f1` (`object_pk`),
  CONSTRAINT `content_type_id_refs_id_79a5c3fa` FOREIGN KEY (`content_type_id`) REFERENCES `django_content_type` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `index_task`
--

LOCK TABLES `index_task` WRITE;
/*!40000 ALTER TABLE `index_task` DISABLE KEYS */;
INSERT INTO `index_task` VALUES (1,14,1,1),(2,14,2,1),(3,14,3,1),(4,14,4,1),(5,14,5,1),(6,14,6,1),(7,14,7,1),(8,14,8,1),(9,14,9,1),(10,14,10,1),(11,14,11,1),(12,14,12,1);
/*!40000 ALTER TABLE `index_task` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `tags_tag`
--

DROP TABLE IF EXISTS `tags_tag`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tags_tag` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(30) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `tags_tag`
--

LOCK TABLES `tags_tag` WRITE;
/*!40000 ALTER TABLE `tags_tag` DISABLE KEYS */;
INSERT INTO `tags_tag` VALUES (15,'android'),(7,'asp.net'),(22,'cookie'),(9,'css'),(12,'django'),(23,'extjs'),(29,'gae'),(18,'ios'),(19,'iphone'),(8,'javascript'),(33,'linux'),(27,'log4net'),(6,'mysql'),(26,'network'),(20,'objective-c'),(25,'oracle'),(5,'pil'),(30,'pyqt'),(2,'python'),(10,'sqlserver'),(34,'thread'),(11,'ubuntu'),(28,'vb.net'),(31,'vim'),(13,'vs2005'),(16,'wsgi'),(21,'xml'),(14,'对话框'),(24,'异常处理'),(32,'排序'),(3,'正则表达式');
/*!40000 ALTER TABLE `tags_tag` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `django_session`
--

DROP TABLE IF EXISTS `django_session`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `django_session` (
  `session_key` varchar(40) NOT NULL,
  `session_data` longtext NOT NULL,
  `expire_date` datetime NOT NULL,
  PRIMARY KEY (`session_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `django_session`
--

LOCK TABLES `django_session` WRITE;
/*!40000 ALTER TABLE `django_session` DISABLE KEYS */;
INSERT INTO `django_session` VALUES ('058a5ebc256d860df429d133b191b79d','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-08-23 13:00:16'),('08603fd1ccfadae37791c0d808594409','NzNkODg3OTBkNTI0YjI0ZDkzZmIyYTZiMjI2OWFlZjhiZDM1YmFmOTqAAn1xAShVAWFLAVUNX2F1\ndGhfdXNlcl9pZIoBAVUSX2F1dGhfdXNlcl9iYWNrZW5kVSlkamFuZ28uY29udHJpYi5hdXRoLmJh\nY2tlbmRzLk1vZGVsQmFja2VuZHUu\n','2011-09-15 23:00:20'),('086de36b67a2efabbd016f778974389a','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-08-23 17:21:44'),('0d0df72ed1aed5a18b24a4cd0df9b70c','NzNkODg3OTBkNTI0YjI0ZDkzZmIyYTZiMjI2OWFlZjhiZDM1YmFmOTqAAn1xAShVAWFLAVUNX2F1\ndGhfdXNlcl9pZIoBAVUSX2F1dGhfdXNlcl9iYWNrZW5kVSlkamFuZ28uY29udHJpYi5hdXRoLmJh\nY2tlbmRzLk1vZGVsQmFja2VuZHUu\n','2012-05-20 21:45:12'),('13787ee96f9983aea693213645674f13','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-28 13:57:43'),('1656cef514be5ba1c57d63fc3bf0390b','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-28 14:47:24'),('1a2310c903c197f24b18e8accfde7e41','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-27 10:46:20'),('1e20a5f0339aa73dc29cf6ecf0501a01','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-09-01 17:33:09'),('1f6d77fafec030001078b3b5c40f4428','MTAzNjc2YjEzNzI4MWI3YTM2MzlkOGIzYzQ1N2VmMzAxZGRkMzU5YjqAAn1xAShVAWFLAVUSX2F1\ndGhfdXNlcl9iYWNrZW5kcQJVKWRqYW5nby5jb250cmliLmF1dGguYmFja2VuZHMuTW9kZWxCYWNr\nZW5kcQNVDV9hdXRoX3VzZXJfaWRxBIoBAXUu\n','2012-06-28 23:51:29'),('263c13a0ebddc8429e8858e196757db6','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-08-19 14:35:26'),('2a5f834d63c202677d5bdf16181bc974','Mjk5OWU4NWRmOTQ0MjdkYjdkNDRmMWM2MjQwMTJmNGNiMzFjMTYyNTqAAn1xAVUBYUsBcy4=\n','2012-06-28 23:53:01'),('2e70e3c7042b50e7ac7fe13a5fc18c47','Mjk5OWU4NWRmOTQ0MjdkYjdkNDRmMWM2MjQwMTJmNGNiMzFjMTYyNTqAAn1xAVUBYUsBcy4=\n','2012-06-28 23:51:52'),('322da7cf452a697da345e30e78cc7ea8','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-28 13:05:08'),('33aea72e21c2904636873145a88c0450','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-08-23 17:15:13'),('36f9e8ef7a19328a945e81213a318dc3','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-28 17:45:29'),('3978c8280b6fc7c78bdb0a3ad1f342c1','NDJkNTYxM2NlM2I0ZTI3YzE2YzEzM2MzZWRiNDAyMjRiNDcyZTYzMTqAAn1xAShVAWFLAVUSX2F1\ndGhfdXNlcl9iYWNrZW5kVSlkamFuZ28uY29udHJpYi5hdXRoLmJhY2tlbmRzLk1vZGVsQmFja2Vu\nZFUNX2F1dGhfdXNlcl9pZIoBAXUu\n','2011-07-20 23:06:25'),('3cd0bd1e6cc857bd0c5d335f064b9c96','gAJ9cQEoVRJfYXV0aF91c2VyX2JhY2tlbmRxAlUpZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5k\ncy5Nb2RlbEJhY2tlbmRxA1UNX2F1dGhfdXNlcl9pZHEEigEBdS45YjBlY2FiZjk2OTBlNmNlZWNk\nMDc2ZjY4YzE1ODViYg==\n','2011-07-05 15:07:13'),('40cfd34d4c41e2c3e37f66ced3053b53','gAJ9cQEoVQFhSwFVDV9hdXRoX3VzZXJfaWRxAooBAVUSX2F1dGhfdXNlcl9iYWNrZW5kcQNVKWRq\nYW5nby5jb250cmliLmF1dGguYmFja2VuZHMuTW9kZWxCYWNrZW5kcQR1LjAyODE3NTFlNTExMTQz\nNTI4ODY5YjE0N2Y4NDAwNTM2\n','2011-09-12 17:29:27'),('45b17d1dd029feb16270af39f653a386','ODdkNWRjNmJiYzBjNTNmNWVhNzEzYzI3OWFkOWE2YWYxNTBlYmM1MzqAAn1xAShVAWFLAVUNX2F1\ndGhfdXNlcl9pZHECigEBVRJfYXV0aF91c2VyX2JhY2tlbmRxA1UpZGphbmdvLmNvbnRyaWIuYXV0\naC5iYWNrZW5kcy5Nb2RlbEJhY2tlbmRxBHUu\n','2012-08-17 22:23:39'),('58f3e4bcf3b2140a2cb04d3dd787766c','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-27 15:11:10'),('59dc5b65816da452779abe3811858d8e','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-26 10:31:58'),('5e89569e85687f99e01f1f5262b0dbc6','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-09-06 15:52:20'),('5f6d07d8fab235d547400d96638f05b9','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-25 17:30:14'),('5f72a330ccf29e956443c645350dee89','gAJ9cQEoVQFhSwFVEl9hdXRoX3VzZXJfYmFja2VuZHECVSlkamFuZ28uY29udHJpYi5hdXRoLmJh\nY2tlbmRzLk1vZGVsQmFja2VuZHEDVQ1fYXV0aF91c2VyX2lkcQSKAQF1LjY1Y2JhYTAxYmJiMGZj\nZjljOWQ2YTk2ZmYxNTNlYTFl\n','2011-09-10 21:21:41'),('671038e0d6b12293a1a7d45819349245','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-27 14:48:40'),('6a49c3da8dbdf997d409bdc4d7a9e17c','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-09-02 13:34:49'),('6b534aefc7fc644a2f4168a20ab20db5','gAJ9cQEoVQFhSwFVDV9hdXRoX3VzZXJfaWRxAooBAVUSX2F1dGhfdXNlcl9iYWNrZW5kcQNVKWRq\nYW5nby5jb250cmliLmF1dGguYmFja2VuZHMuTW9kZWxCYWNrZW5kcQR1LjAyODE3NTFlNTExMTQz\nNTI4ODY5YjE0N2Y4NDAwNTM2\n','2011-07-28 18:14:23'),('70001a247d6075a94f0473801b353176','MTAzNjc2YjEzNzI4MWI3YTM2MzlkOGIzYzQ1N2VmMzAxZGRkMzU5YjqAAn1xAShVAWFLAVUSX2F1\ndGhfdXNlcl9iYWNrZW5kcQJVKWRqYW5nby5jb250cmliLmF1dGguYmFja2VuZHMuTW9kZWxCYWNr\nZW5kcQNVDV9hdXRoX3VzZXJfaWRxBIoBAXUu\n','2011-11-23 23:31:55'),('74f6a3f1f027413eca081a9f10f2baf7','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-08-23 10:15:07'),('76dbb4220817d94a555098b476a507d6','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-08-18 13:31:35'),('801058383da5280edbaaf3d2a8f4f191','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-27 11:03:58'),('881ff8278e21d168c0cd5a6d8e666a52','Mjk5OWU4NWRmOTQ0MjdkYjdkNDRmMWM2MjQwMTJmNGNiMzFjMTYyNTqAAn1xAVUBYUsBcy4=\n','2012-09-08 02:53:42'),('8a1a002935615dd42ec3a42f57461fd9','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-09-06 11:42:47'),('9208157ee3d3f911a610956e7a69daa5','MzZkYjZiMmVjNmM2ZjhkNDUxZTBlYWU4ZDljMGNjZTc2ZjlhNGU2NTqAAn1xAS4=\n','2012-05-27 15:48:09'),('94a9570850f53ddab38a22e90f732a17','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-08-19 11:36:04'),('9827dc9285995a16be0aac380cfdfb70','Mjk5OWU4NWRmOTQ0MjdkYjdkNDRmMWM2MjQwMTJmNGNiMzFjMTYyNTqAAn1xAVUBYUsBcy4=\n','2012-04-12 22:55:07'),('9bdedd8d3129c20fd559a4cca542051e','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-25 17:29:28'),('9e9621625f5e74fd1eb822668b8b492d','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-08-23 09:02:53'),('9fe9fe0509caae828952528e03e910f8','gAJ9cQEoVRJfYXV0aF91c2VyX2JhY2tlbmRxAlUpZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5k\ncy5Nb2RlbEJhY2tlbmRxA1UNX2F1dGhfdXNlcl9pZHEEigEBdS45YjBlY2FiZjk2OTBlNmNlZWNk\nMDc2ZjY4YzE1ODViYg==\n','2011-08-23 15:04:02'),('a1524fdfc8daf63aedfc057587d79974','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-26 16:45:27'),('a2e50ef198b7297ce1a02b6687a376dc','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-06 15:44:02'),('a75e3ce6042fecfcfecda840d513c896','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-08-19 16:11:12'),('a909e1e9f762ca868d90c6c8448d0ce7','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-08-23 17:08:44'),('ab714e5b019e7c7fb712e0198d576397','ZjExZmM0MWRhMjFkOGZmZGM5OTU4MWM4YTQzNmJjMDA5ZDU5ZThkNTqAAn1xAShVEl9hdXRoX3Vz\nZXJfYmFja2VuZHECVSlkamFuZ28uY29udHJpYi5hdXRoLmJhY2tlbmRzLk1vZGVsQmFja2VuZHED\nVQ1fYXV0aF91c2VyX2lkcQSKAQF1Lg==\n','2012-05-27 16:18:27'),('ac4270744d005382798a6dfd76ded2d9','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-28 17:51:15'),('afd10193ef2e5fb4260fc5903c9c3f4a','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-27 14:09:00'),('be4f5493f7b6f8e39da7c6e832e3c072','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-21 20:01:44'),('c0ff8aa88d51d0e7025e5179b8d0c10a','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-21 20:35:13'),('c235c97c0bcc79f559420f4ca12508bf','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-25 17:45:04'),('c3365fec964c2259a7454446b0257217','Mjk5OWU4NWRmOTQ0MjdkYjdkNDRmMWM2MjQwMTJmNGNiMzFjMTYyNTqAAn1xAVUBYUsBcy4=\n','2012-01-28 10:29:39'),('c3e5e8b2b439c4636aa539d6a0bf6dee','ZjExZmM0MWRhMjFkOGZmZGM5OTU4MWM4YTQzNmJjMDA5ZDU5ZThkNTqAAn1xAShVEl9hdXRoX3Vz\nZXJfYmFja2VuZHECVSlkamFuZ28uY29udHJpYi5hdXRoLmJhY2tlbmRzLk1vZGVsQmFja2VuZHED\nVQ1fYXV0aF91c2VyX2lkcQSKAQF1Lg==\n','2012-03-23 23:53:54'),('c443cd7e3aeab07f767d502af24dd61d','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-08-19 09:07:33'),('ccd74b46ab3d94b031750716f720c1ca','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-28 10:05:18'),('cf692dede61f6e1e01133d828450ce78','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-28 13:04:17'),('d121cc7c30e132eb0cb9604d75d9e15e','gAJ9cQEoVRJfYXV0aF91c2VyX2JhY2tlbmRxAlUpZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5k\ncy5Nb2RlbEJhY2tlbmRxA1UNX2F1dGhfdXNlcl9pZHEEigEBdS45YjBlY2FiZjk2OTBlNmNlZWNk\nMDc2ZjY4YzE1ODViYg==\n','2011-07-05 11:43:07'),('d23b2597ada46384d9d640cb6eede4e0','gAJ9cQEoVQFhSwFVEl9hdXRoX3VzZXJfYmFja2VuZHECVSlkamFuZ28uY29udHJpYi5hdXRoLmJh\nY2tlbmRzLk1vZGVsQmFja2VuZHEDVQ1fYXV0aF91c2VyX2lkcQSKAQF1LjY1Y2JhYTAxYmJiMGZj\nZjljOWQ2YTk2ZmYxNTNlYTFl\n','2011-07-21 20:00:04'),('d5632fbe1e5d32a34d55cafc6e153589','ODdkNWRjNmJiYzBjNTNmNWVhNzEzYzI3OWFkOWE2YWYxNTBlYmM1MzqAAn1xAShVAWFLAVUNX2F1\ndGhfdXNlcl9pZHECigEBVRJfYXV0aF91c2VyX2JhY2tlbmRxA1UpZGphbmdvLmNvbnRyaWIuYXV0\naC5iYWNrZW5kcy5Nb2RlbEJhY2tlbmRxBHUu\n','2012-02-26 22:28:35'),('d84a3178d84f97a74a5eaaabcb1d5130','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-08-22 16:02:06'),('e173de74a8fe437651f5c6287dc2c14f','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-25 10:07:45'),('e194ca42fbbb9caaffc6e6e288bad4ce','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-08-23 15:41:17'),('e5504c72e4f5576039e9d5174da72170','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-25 11:36:30'),('e7cd1f5767bcb69cf1f2135cfb61d981','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-08-31 17:06:08'),('e8e4c944ead93add28ee4b42e4ef6dd4','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-28 13:56:54'),('ef7e639bdcd0a29eb66f7c824cd7155a','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-08-23 08:49:22'),('f09cdc78b771524faca2c73f52ff27f7','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-08-18 10:06:57'),('f1eaef7ac633219278fa1fbbae992d27','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-28 12:39:25'),('f8c198b5f3933dca749736b39a26ee1f','gAJ9cQFVAWFLAXMuMDFmMmRkNjgyNzIwMzIzNGQxNjgzMmExN2JmZmNmMmM=\n','2011-07-28 12:35:23'),('f995e191589705ffc17de4a6bc1aa7e0','NzNkODg3OTBkNTI0YjI0ZDkzZmIyYTZiMjI2OWFlZjhiZDM1YmFmOTqAAn1xAShVAWFLAVUNX2F1\ndGhfdXNlcl9pZIoBAVUSX2F1dGhfdXNlcl9iYWNrZW5kVSlkamFuZ28uY29udHJpYi5hdXRoLmJh\nY2tlbmRzLk1vZGVsQmFja2VuZHUu\n','2011-11-12 23:44:01');
/*!40000 ALTER TABLE `django_session` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `auth_user_groups`
--

DROP TABLE IF EXISTS `auth_user_groups`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `auth_user_groups` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `group_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_id` (`user_id`,`group_id`),
  KEY `auth_user_groups_403f60f` (`user_id`),
  KEY `auth_user_groups_425ae3c4` (`group_id`),
  CONSTRAINT `group_id_refs_id_f116770` FOREIGN KEY (`group_id`) REFERENCES `auth_group` (`id`),
  CONSTRAINT `user_id_refs_id_7ceef80f` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `auth_user_groups`
--

LOCK TABLES `auth_user_groups` WRITE;
/*!40000 ALTER TABLE `auth_user_groups` DISABLE KEYS */;
/*!40000 ALTER TABLE `auth_user_groups` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `comments_comment`
--

DROP TABLE IF EXISTS `comments_comment`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `comments_comment` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) DEFAULT NULL,
  `user_name` varchar(50) NOT NULL,
  `user_email` varchar(75) NOT NULL,
  `user_url` varchar(200) NOT NULL,
  `comment` longtext NOT NULL,
  `submit_date` datetime NOT NULL,
  `ip_address` char(15) DEFAULT NULL,
  `is_public` tinyint(1) NOT NULL,
  `is_removed` tinyint(1) NOT NULL,
  `content_type_id` int(11) NOT NULL,
  `object_pk` longtext NOT NULL,
  `site_id` int(11) NOT NULL,
  `parent_id` int(11) DEFAULT NULL,
  `is_mailnotify` tinyint(1) NOT NULL,
  `pertain` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `comments_comment_403f60f` (`user_id`),
  KEY `comments_comment_1bb8f392` (`content_type_id`),
  KEY `comments_comment_6223029` (`site_id`),
  KEY `comments_comment_63f17a16` (`parent_id`),
  CONSTRAINT `content_type_id_refs_id_4c03983c` FOREIGN KEY (`content_type_id`) REFERENCES `django_content_type` (`id`),
  CONSTRAINT `parent_id_refs_id_3b4faabd` FOREIGN KEY (`parent_id`) REFERENCES `comments_comment` (`id`),
  CONSTRAINT `site_id_refs_id_5783d6af` FOREIGN KEY (`site_id`) REFERENCES `django_site` (`id`),
  CONSTRAINT `user_id_refs_id_508b97b8` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `comments_comment`
--

LOCK TABLES `comments_comment` WRITE;
/*!40000 ALTER TABLE `comments_comment` DISABLE KEYS */;
INSERT INTO `comments_comment` VALUES (1,1,'ywjsailor','ywjsailor@163.com','','dddd','2011-06-21 13:23:54','127.0.0.1',1,0,15,'16',1,NULL,0,1),(2,1,'ywjsailor','ywjsailor@163.com','','d','2011-06-21 13:58:23','127.0.0.1',1,0,17,'1',1,NULL,0,1),(3,1,'ywjsailor','ywjsailor@163.com','','asdf','2011-06-21 13:59:00','127.0.0.1',1,0,15,'16',1,NULL,0,1),(4,NULL,'asdf','ywjsailor@gami.com','','/bin/bash: jar:找不到命令','2011-08-09 11:16:27','10.20.140.151',1,0,15,'20',1,NULL,0,1);
/*!40000 ALTER TABLE `comments_comment` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `auth_message`
--

DROP TABLE IF EXISTS `auth_message`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `auth_message` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `message` longtext NOT NULL,
  PRIMARY KEY (`id`),
  KEY `auth_message_403f60f` (`user_id`),
  CONSTRAINT `user_id_refs_id_650f49a6` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `auth_message`
--

LOCK TABLES `auth_message` WRITE;
/*!40000 ALTER TABLE `auth_message` DISABLE KEYS */;
/*!40000 ALTER TABLE `auth_message` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `blog_article`
--

DROP TABLE IF EXISTS `blog_article`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `blog_article` (
  `id` int(11) NOT NULL,
  `title` varchar(50) NOT NULL,
  `slug` varchar(50) NOT NULL,
  `blog_id` int(11) NOT NULL,
  `contents` longtext NOT NULL,
  `summary` longtext,
  `is_published` tinyint(1) NOT NULL,
  `allow_comment` tinyint(1) NOT NULL,
  `created_date` datetime NOT NULL,
  `updated_date` datetime NOT NULL,
  `category_id` int(11) NOT NULL,
  `tags` varchar(255) DEFAULT NULL,
  `browse` int(11) DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `blog_id` (`blog_id`,`slug`),
  KEY `blog_article_56ae2a2a` (`slug`),
  KEY `blog_article_472bc96c` (`blog_id`),
  KEY `blog_article_42dc49bc` (`category_id`),
  CONSTRAINT `blog_id_refs_id_c0f7b72` FOREIGN KEY (`blog_id`) REFERENCES `blog_info` (`id`),
  CONSTRAINT `category_id_refs_id_25820696` FOREIGN KEY (`category_id`) REFERENCES `blog_category` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `blog_article`
--

LOCK TABLES `blog_article` WRITE;
/*!40000 ALTER TABLE `blog_article` DISABLE KEYS */;
INSERT INTO `blog_article` VALUES (1,'Python装饰器学习','python-decorator',1,'<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">1. 这是个什么东东？</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">书上说得不是太清楚，它说类似于Java的AOP(Aspect Oriented Programming，面向方面编程)，我对AOP一无所知。根据我对许多例子用法的反复揣摩，我认为是类似于程序设计语义学中所说的前键 与后键 的概念(Eiffel中的@pre 与@post )。当然你可以在装饰器中做比前键与后键更多的事，如：引入日志、增加计时逻辑来检测性能、给函数增加事务的能力。</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">其实总体说起来，装饰器其实也就是一个函数，一个用来包装函数的函数，返回一个修改之后的函数对象，将其重新赋值原来的标识符，并永久丧失对原始函数对象的访问。</div>\r\n<p>1. 这是个什么东东？</p>\r\n<p>书上说得不是太清楚，它说类似于Java的AOP(Aspect Oriented Programming，面向方面编程)，我对AOP一无所知。根据我对许多例子用法的反复揣摩，我认为是类似于程序设计语义学中所说的前键 与后键 的概念(Eiffel中的@pre 与@post )。当然你可以在装饰器中做比前键与后键更多的事，如：引入日志、增加计时逻辑来检测性能、给函数增加事务的能力。<br />其实总体说起来，装饰器其实也就是一个函数，一个用来包装函数的函数，返回一个修改之后的函数对象，将其重新赋值原来的标识符，并永久丧失对原始函数对象的访问。</p>\r\n<p>2. 装饰器语法</p>\r\n<pre class=\"prettyprint\">def deco(func):  \r\n    print func  \r\n    return func  \r\n@deco  \r\ndef foo():pass  \r\nfoo()  </pre>\r\n<p>第一个函数deco是装饰函数，它的参数就是被装饰的函数对象。我们可以在deco函数内对传入的函数对象做一番&ldquo;装饰&rdquo;，然后返回这个对象（记住一定要返回 ，不然外面调用foo的地方将会无函数可用。实际上此时foo=deco(foo)）</p>\r\n<p>我写了个小例子，检查函数有没有说明文档：</p>\r\n<pre class=\"prettyprint\">def deco_functionNeedDoc(func):\r\n    if func.__doc__ == None :\r\n        print func, \"has no __doc__, it\'s a bad habit.\"\r\n    else:\r\n        print func, \':\', func.__doc__, \'.\'\r\n    return func\r\n@deco_functionNeedDoc\r\ndef f():\r\n    print \'f() Do something\'\r\n@deco_functionNeedDoc\r\ndef g():\r\n    \'I have a __doc__\'\r\n    print \'g() Do something\'\r\nf()\r\ng()</pre>\r\n<p>(2)有参数装饰器</p>\r\n<pre class=\"prettyprint\">def decomaker(arg):\r\n    \'通常对arg会有一定的要求\'\r\n    \"\"\"由于有参数的decorator函数在调用时只会使用应用时的参数\r\n	而不接收被装饰的函数做为参数，所以必须在其内部再创建\r\n	一个函数\r\n    \"\"\"\r\n    def newDeco(func):    #定义一个新的decorator函数\r\n		print func, arg\r\n		return func\r\n	return newDeco\r\n@decomaker(deco_args)\r\ndef foo():pass\r\nfoo()</pre>\r\n<div>第一个函数decomaker是装饰函数，它的参数是用来加强&ldquo;加强装饰&rdquo;的。由于此函数并非被装饰的函数对象，所以在内部必须至少创建一个接受被装饰函数的函数，然后返回这个对象（实际上此时foo=decomaker(arg)(foo)）</div>\r\n<div>这个我还真想不出什么好例子，还是见识少啊，只好借用同步锁的例子了：</div>\r\n<div>\r\n<pre class=\"prettyprint\">def synchronized(lock):\r\n    \"\"\"锁同步装饰方法\r\n    ！lock必须实现了acquire和release方法\r\n    \"\"\"\r\n    def sync_with_lock(func):\r\n        def new_func(*args, **kwargs):\r\n            lock.acquire()\r\n            try:\r\n                return func(*args, **kwargs)\r\n            finally:\r\n                lock.release()\r\n        new_func.func_name = func.func_name\r\n        new_func.__doc__ = func.__doc__\r\n        return new_func\r\n    return sync_with_lock\r\n@synchronized(__locker)\r\ndef update(data):\r\n    \"\"\"更新计划任务\"\"\"\r\n    tasks = self.get_tasks()\r\n    delete_task = None\r\n    for task in tasks:\r\n        if task[PLANTASK.ID] == data[PLANTASK.ID]:\r\n            tasks.insert(tasks.index(task), data)\r\n            tasks.remove(task)\r\n            delete_task = task\r\n    r, msg = self._refresh(tasks, delete_task)\r\n    return r, msg, data[PLANTASK.ID]</pre>\r\n<div>调用时还是updae(data)。</div>\r\n<div>同时还可以将多个装饰器组合 使用：\r\n<pre class=\"prettyprint\">@synchronized(__locker)\r\n@deco_functionNeedDoc\r\ndef f():\r\n    print \'f() Do something\'</pre>\r\n学后的总是感觉就是：装饰器可以让函数轻装上阵，更重要的是将函数的约束放置于接口处，使意图更加明了，同时又不增加调用者的负担。</div>\r\n</div>','',1,0,'2011-05-15 15:49:44','2011-05-15 15:56:09',2,'Python',0),(2,'正则表达式','regular-expression',1,'<p><strong>1. 元字符</strong></p>\r\n<table style=\"font-family: Arial; font-size: 12px;\" cellspacing=\"0\">\r\n<tbody>\r\n<tr>\r\n<th style=\"width: 60px;\">代码</th><th>说明</th>\r\n</tr>\r\n<tr>\r\n<td>.</td>\r\n<td>匹配除换行符以外的任意字符</td>\r\n</tr>\r\n<tr>\r\n<td>\\w</td>\r\n<td>匹配字母或数字或下划线</td>\r\n</tr>\r\n<tr>\r\n<td>\\s</td>\r\n<td>匹配任意的空白符，包括换行符\\n、回车符\\r、制表符\\t、垂直制表符\\v、换页符\\f</td>\r\n</tr>\r\n<tr>\r\n<td>\\d</td>\r\n<td>匹配数字</td>\r\n</tr>\r\n<tr>\r\n<td>\\b</td>\r\n<td>匹配单词的开始或结束</td>\r\n</tr>\r\n<tr>\r\n<td>^</td>\r\n<td>匹配字符串的开始</td>\r\n</tr>\r\n<tr>\r\n<td>$</td>\r\n<td>匹配字符串的结束</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>下面来看看更多的例子：</p>\r\n<p><span style=\"color: #ff0000;\">&nbsp; &nbsp; &nbsp; \\ba\\w*\\b</span>匹配以字母<span style=\"color: #008000;\">a</span>开头的单词&mdash;&mdash;先是某个单词开始处(<span style=\"color: #008000;\">\\b</span>)，然后是字母<span style=\"color: #008000;\">a</span>,然后是任意数量的字母或数字(<span style=\"color: #008000;\">\\w*</span>)，最后是单词结束处(<span style=\"color: #008000;\">\\b</span>)。<br /><span style=\"color: #ff0000;\">&nbsp; &nbsp; &nbsp; \\d+</span>匹配1个或更多连续的数字。这里的<span style=\"color: #008000;\">+</span>是和<span style=\"color: #008000;\">*</span>类似的元字符，不同的是*匹配重复任意次(可能是0次)，而<span style=\"color: #008000;\">+</span>则匹配重复1次或更多次。<br /><span style=\"color: #ff0000;\">&nbsp; &nbsp; &nbsp; \\b\\w{6}\\b</span> 匹配刚好6个字符的单词</p>\r\n<p>&nbsp; &nbsp; &nbsp; 元字符<span style=\"color: #008000;\">^</span>（和数字6在同一个键位上的符号）和<span style=\"color: #008000;\">$</span>都匹配一个位置，这和\\b有点类似。^匹配你要用来查找的字符串的开头，$匹配结尾。这两个代码在验证输入的内容时非常有用，比如一个网站如果要求你填写的QQ号必须为5位到12位数字时，可以使用：<span style=\"color: #ff0000;\">^\\d{5,12}$</span>。<br />&nbsp; &nbsp; &nbsp; 这里的<span style=\"color: #008000;\">{5,12}</span>和前面介绍过的<span style=\"color: #008000;\">{6}</span>是类似的，只不过<span style=\"color: #008000;\">{6}</span>匹配只能不多不少重复6次，<span style=\"color: #008000;\">{5,12}</span>则是重复的次数不能少于5次，不能多于12次，否则都不匹配。</p>\r\n<p><strong>2. 字符转义</strong></p>\r\n<p>&nbsp; &nbsp; &nbsp; 如果你想查找元字符本身的话，比如你查找.,或者*,就出现了问题：你没办法指定它们，因为它们会被解释成别的意思。这时你就得使用 <span style=\"color: #339966;\">\\</span> 来取消这些字符的特殊意义。因此，你应该使用<span style=\"color: #ff0000;\">\\.</span>和<span style=\"color: #ff0000;\">\\*</span>。当然，要查找<span style=\"color: #ff0000;\">\\</span>本身，你也得用<span style=\"color: #ff0000;\">\\\\</span>.</p>\r\n<p>&nbsp; &nbsp; &nbsp; 例如：<span style=\"color: #ff0000;\">unibetter\\.com</span>匹配unibetter.com，<span style=\"color: #ff0000;\">C:\\\\Windows</span>匹配C:\\Windows。</p>\r\n<p><strong>3. 重复</strong></p>\r\n<table style=\"font-family: Arial; font-size: 12px;\" cellspacing=\"0\">\r\n<tbody>\r\n<tr>\r\n<th style=\"width: 60px;\">代码/语法</th><th>说明</th>\r\n</tr>\r\n<tr>\r\n<td>*</td>\r\n<td>重复零次或更多次</td>\r\n</tr>\r\n<tr>\r\n<td>+</td>\r\n<td>重复一次或更多次</td>\r\n</tr>\r\n<tr>\r\n<td>?</td>\r\n<td>重复零次或一次</td>\r\n</tr>\r\n<tr>\r\n<td>{n}</td>\r\n<td>重复n次</td>\r\n</tr>\r\n<tr>\r\n<td>{n,}</td>\r\n<td>重复n次或更多次</td>\r\n</tr>\r\n<tr>\r\n<td>{n,m}</td>\r\n<td>重复n到m次</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>下面是一些使用重复的例子：<br />&nbsp; &nbsp; &nbsp; <span style=\"color: #ff0000;\">Windows\\d+</span>匹配Windows后面跟1个或更多数字<br />&nbsp; &nbsp; &nbsp; <span style=\"color: #ff0000;\">^\\w+</span>匹配一行的第一个单词(或整个字符串的第一个单词，具体匹配哪个意思得看选项设置)</p>\r\n<p><strong>4. 字符类</strong></p>\r\n<p>&nbsp; &nbsp; &nbsp; 要想查找数字，字母或数字，空白是很简单的，因为已经有了对应这些字符集合的元字符，但是如果你想匹配没有预定义元字符的字符集合(比如元音字母a,e,i,o,u),应该怎么办？<br />&nbsp; &nbsp; &nbsp; 很简单，你只需要在方括号里列出它们就行了，像<span style=\"color: #ff0000;\">[aeiou]</span>就匹配任何一个英文元音字母，<span style=\"color: #ff0000;\">[.?!]</span>匹配标点符号(.或?或!)。</p>\r\n<p>&nbsp; &nbsp; &nbsp; 我们也可以轻松地指定一个字符<strong>范围</strong>，像<span style=\"color: #ff0000;\">[0-9]</span>代表的含意与<span style=\"color: #ff0000;\">\\d</span>就是完全一致的：一位数字；同理<span style=\"color: #ff0000;\">[a-z0-9A-Z_]</span>也完全等同于<span style=\"color: #339966;\">\\w</span>（如果只考虑英文的话）。</p>\r\n<p>下面是一个更复杂的表达式：<span style=\"color: #ff0000;\">\\(?0\\d{2}[) -]?\\d{8}</span>。 <br />&nbsp; &nbsp; &nbsp; 这个表达式可以匹配几种格式的电话号码，像(010)88886666，或022-22334455，或02912345678等。我们对它进行一些分析吧：首先是一个转义字符<span style=\"color: #008000;\">\\(</span>,它能出现0次或1次(<span style=\"color: #008000;\">?</span>),然后是一个<span style=\"color: #008000;\">0</span>，后面跟着2个数字(<span style=\"color: #008000;\">\\d{2}</span>)，然后是<span style=\"color: #008000;\">)</span>或<span style=\"color: #008000;\">-</span>或<span style=\"color: #008000;\">空格</span>中的一个，它出现1次或不出现(<span style=\"color: #008000;\">?</span>)，最后是8个数字(<span style=\"color: #008000;\">\\d{8}</span>)。</p>\r\n<p><strong>5. 分枝条件</strong></p>\r\n<p>&nbsp; &nbsp; &nbsp; 不幸的是，刚才那个表达式也能匹配010)12345678或(022-87654321这样的&ldquo;不正确&rdquo;的格式。要解决这个问题，我们需要用到分枝条件。正则表达式里的分枝条件指的是有几种规则，如果满足其中任意一种规则都应该当成匹配，具体方法是用 <span style=\"color: #ff0000;\">|</span> 把不同的规则分隔开。例子：</p>\r\n<p>&nbsp; &nbsp; &nbsp; <span style=\"color: #ff0000;\">0\\d{2}-\\d{8}|0\\d{3}-\\d{7}</span>这个表达式能匹配两种以连字号分隔的电话号码：一种是三位区号，8位本地号(如010-12345678)，一种是4位区号，7位本地号(0376-2233445)。<br />&nbsp; &nbsp; &nbsp; <span style=\"color: #ff0000;\">\\(0\\d{2}\\)[- ]?\\d{8}|0\\d{2}[- ]?\\d{8}</span>这个表达式匹配3位区号的电话号码，其中区号可以用小括号括起来，也可以不用，区号与本地号间可以用连字号或空格间隔，也可以没有间隔。你可以试试用分枝条件把这个表达式扩展成也支持4位区号的。<br />&nbsp; &nbsp; &nbsp; <span style=\"color: #ff0000;\">\\d{5}-\\d{4}|\\d{5}</span>这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字，或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题：使用分枝条件时，要注意各个条件的顺序。如果你把它改成<span style=\"color: #ff0000;\">\\d{5}|\\d{5}-\\d{4}</span>的话，那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时，将会从左到右地测试每个条件，如果满足了某个分枝的话，就不会去再管其它的条件了。</p>\r\n<p><strong>6. 分组</strong></p>\r\n<p>&nbsp; &nbsp; &nbsp; 我们已经提到了怎么重复单个字符（直接在字符后面加上限定符就行了）；但如果想要重复多个字符又该怎么办？你可以用小括号来指定<strong>子表达式</strong>(也叫做<strong>分组</strong>)，然后你就可以指定这个子表达式的重复次数了，你也可以对子表达式进行其它一些操作(后面会有介绍)。</p>\r\n<p>&nbsp; &nbsp; &nbsp; <span style=\"color: #ff0000;\">(\\d{1,3}\\.){3}\\d{1,3}</span>是一个简单的IP地址匹配表达式。要理解这个表达式，请按下列顺序分析它：<span style=\"color: #008000;\">\\d{1,3}</span>匹配1到3位的数字，<span style=\"color: #008000;\">(\\d{1,3}\\.){3}</span>匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次，最后再加上一个一到三位的数字<span style=\"color: #008000;\">(\\d{1,3})</span>。</p>\r\n<p>&nbsp; &nbsp; &nbsp; 不幸的是，它也将匹配256.300.888.999这种不可能存在的IP地址。如果能使用算术比较的话，或许能简单地解决这个问题，但是正则表达式中并不提供关于数学的任何功能，所以只能使用冗长的分组，选择，字符类来描述一个正确的IP地址：<span style=\"color: #ff0000;\">((2[0-4]\\d|25[0-5]|[01]?\\d\\d?)\\.){3}(2[0-4]\\d|25[0-5]|[01]?\\d\\d?)</span>。</p>\r\n<p>&nbsp; &nbsp; &nbsp; 理解这个表达式的关键是理解<span style=\"color: #008000;\">2[0-4]\\d|25[0-5]|[01]?\\d\\d?</span>，这里我就不细说了，你自己应该能分析得出来它的意义。</p>\r\n<p><strong>7. 反义</strong></p>\r\n<table style=\"font-family: Arial; font-size: 12px;\" cellspacing=\"0\">\r\n<tbody>\r\n<tr>\r\n<th style=\"width: 60px;\">代码/语法</th><th>说明</th>\r\n</tr>\r\n<tr>\r\n<td>\\W</td>\r\n<td>匹配任意不是字母，数字，下划线，汉字的字符</td>\r\n</tr>\r\n<tr>\r\n<td>\\S</td>\r\n<td>匹配任意不是空白符的字符</td>\r\n</tr>\r\n<tr>\r\n<td>\\D</td>\r\n<td>匹配任意非数字的字符</td>\r\n</tr>\r\n<tr>\r\n<td>\\B</td>\r\n<td>匹配不是单词开头或结束的位置</td>\r\n</tr>\r\n<tr>\r\n<td>[^x]</td>\r\n<td>匹配除了x以外的任意字符</td>\r\n</tr>\r\n<tr>\r\n<td>[^aeiou]</td>\r\n<td>匹配除了aeiou这几个字母以外的任意字符</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>&nbsp; &nbsp; &nbsp; 有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外，其它任意字符都行的情况，这时需要用到<strong>反义</strong>：</p>\r\n<p>例子： <br />&nbsp; &nbsp; &nbsp; <span style=\"color: #ff0000;\">\\S+</span>匹配不包含空白符的字符串。<br />&nbsp; &nbsp; &nbsp; <span style=\"color: #ff0000;\">]+&gt;</span>匹配用尖括号括起来的以a开头的字符串。</p>\r\n<p><strong>8. 匹配实例</strong></p>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td style=\"width: 300px;\">只能输入数字</td>\r\n<td>^[0-9]*$</td>\r\n</tr>\r\n<tr>\r\n<td>只能输入n位的数字</td>\r\n<td>^\\d{n}$</td>\r\n</tr>\r\n<tr>\r\n<td>只能输入至少n位的数字</td>\r\n<td>^\\d{n,}$</td>\r\n</tr>\r\n<tr>\r\n<td>只能输入m~n位的数字</td>\r\n<td>^\\d{m,n}$</td>\r\n</tr>\r\n<tr>\r\n<td>只能输入零和非零开头的数字</td>\r\n<td>^(0|[1-9][0-9]*)$</td>\r\n</tr>\r\n<tr>\r\n<td>只能输入有两位小数的正实数</td>\r\n<td>^[0-9]+(.[0-9]{2})?$</td>\r\n</tr>\r\n<tr>\r\n<td>只能输入有1~3位小数的正实数</td>\r\n<td>^[0-9]+(.[0-9]{1,3})?$</td>\r\n</tr>\r\n<tr>\r\n<td>只能输入非零的正整数</td>\r\n<td>^\\+?[1-9][0-9]*$</td>\r\n</tr>\r\n<tr>\r\n<td>只能输入非零的负整数</td>\r\n<td>^\\-[1-9][0-9]*$</td>\r\n</tr>\r\n<tr>\r\n<td>只能输入长度为3的字符</td>\r\n<td>^.{3}$</td>\r\n</tr>\r\n<tr>\r\n<td>只能输入由26个英文字母组成的字符串</td>\r\n<td>^[A-Za-z]+$</td>\r\n</tr>\r\n<tr>\r\n<td>只能输入由26个大写英文字母组成的字符串</td>\r\n<td>^[A-Z]+$</td>\r\n</tr>\r\n<tr>\r\n<td>只能输入由26个小写英文字母组成的字符串</td>\r\n<td>^[a-z]+$</td>\r\n</tr>\r\n<tr>\r\n<td>只能输入由数字和26个英文字母组成的字符串</td>\r\n<td>^[A-Za-z0-9]+$</td>\r\n</tr>\r\n<tr>\r\n<td>只能输入由数字、26个英文字母或者下划线组成的字符串</td>\r\n<td>^\\w+$</td>\r\n</tr>\r\n<tr>\r\n<td>验证用户密码</td>\r\n<td>\"^[a-zA-Z]\\w{5,17}$\"<br />正确格式为：以字母开头，长度在6~18之间，只能包含字符、数字和下划线。</td>\r\n</tr>\r\n<tr>\r\n<td>验证是否含有^%&amp;\',;=?$\\\"等字符</td>\r\n<td>[^%&amp;\',;=?$\\x22]+</td>\r\n</tr>\r\n<tr>\r\n<td>只能输入汉字</td>\r\n<td>^[\\u4e00-\\u9fa5]{0,}$</td>\r\n</tr>\r\n<tr>\r\n<td>验证Email地址</td>\r\n<td>^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$</td>\r\n</tr>\r\n<tr>\r\n<td>验证InternetURL</td>\r\n<td>^http://([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&amp;=]*)?$</td>\r\n</tr>\r\n<tr>\r\n<td>验证电话号码</td>\r\n<td>\"^(\\(\\d{3,4}-)|\\d{3.4}-)?\\d{7,8}$\"<br />正确格式为：\"XXX-XXXXXXX\"、\"XXXX- XXXXXXXX\"、\"XXX-XXXXXXX\"、\"XXX-XXXXXXXX\"、\"XXXXXXX\"和\"XXXXXXXX\"。</td>\r\n</tr>\r\n<tr>\r\n<td>验证身份证号（15位或18位数字）</td>\r\n<td>^\\d{15}|\\d{18}$</td>\r\n</tr>\r\n<tr>\r\n<td>验证一年的12个月</td>\r\n<td>\"^(0?[1-9]|1[0-2])$\"<br />正确格式为：\"01\"～\"09\"和\"1\"～\"12\"。</td>\r\n</tr>\r\n<tr>\r\n<td>验证一个月的31天</td>\r\n<td>\"^((0?[1-9])|((1|2)[0-9])|30|31)$\"<br />正确格式为；\"01\"～\"09\"和\"1\"～\"31\"。</td>\r\n</tr>\r\n<tr>\r\n<td>匹配 0.1~10.0 之间的数字</td>\r\n<td>^((0\\.[1-9]?)|([1-9](\\.\\d)?)|(10)|(10.0))$</td>\r\n</tr>\r\n</tbody>\r\n</table>','',1,0,'2011-05-15 16:37:19','2011-05-15 17:07:15',1,'正则表达式',NULL),(3,'Python中*args和**kwargs','python-args-kwargs',1,'<p>先来看个例子：</p>\r\n<pre class=\"prettyprint\">def foo(*args, **kwargs):\r\n    print \'args = \', args\r\n    print \'kwargs = \', kwargs\r\n    print \'---------------------------------------\'\r\n\r\nif __name__ == \'__main__\':\r\n    foo(1,2,3,4)\r\n    foo(a=1,b=2,c=3)\r\n    foo(1,2,3,4, a=1,b=2,c=3)\r\n    foo(\'a\', 1, None, a=1, b=\'2\', c=3)</pre>\r\n<p>输出结果如下：</p>\r\n<p>args =&nbsp; (1, 2, 3, 4)<br style=\"padding: 0px; margin: 0px;\" />kwargs =&nbsp; {}<br style=\"padding: 0px; margin: 0px;\" />&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;<br style=\"padding: 0px; margin: 0px;\" />args =&nbsp; ()<br style=\"padding: 0px; margin: 0px;\" />kwargs =&nbsp; {&lsquo;a&rsquo;: 1, &lsquo;c&rsquo;: 3, &lsquo;b&rsquo;: 2}<br style=\"padding: 0px; margin: 0px;\" />&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;<br style=\"padding: 0px; margin: 0px;\" />args =&nbsp; (1, 2, 3, 4)<br style=\"padding: 0px; margin: 0px;\" />kwargs =&nbsp; {&lsquo;a&rsquo;: 1, &lsquo;c&rsquo;: 3, &lsquo;b&rsquo;: 2}<br style=\"padding: 0px; margin: 0px;\" />&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;<br style=\"padding: 0px; margin: 0px;\" />args =&nbsp; (&lsquo;a&rsquo;, 1, None)<br style=\"padding: 0px; margin: 0px;\" />kwargs =&nbsp; {&lsquo;a&rsquo;: 1, &lsquo;c&rsquo;: 3, &lsquo;b&rsquo;: &rsquo;2&prime;}<br style=\"padding: 0px; margin: 0px;\" />&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;</p>\r\n<p>可以看到，这两个是python中的可变参数。*args表示任何多个无名参数，它是一个tuple；**kwargs表示关键字参数，它是一个dict。并且同时使用*args和**kwargs时，必须*args参数列要在**kwargs前，像foo(a=1, b=&rsquo;2&prime;, c=3, a&rsquo;, 1, None, )这样调用的话，会提示语法错误&ldquo;SyntaxError: non-keyword arg after keyword arg&rdquo;。</p>\r\n<p>呵呵，知道*args和**kwargs是什么了吧。还有一个很漂亮的用法，就是创建字典：</p>\r\n<pre class=\"prettyprint\">def kw_dict(**kwargs):\r\n        return kwargs\r\n    print kw_dict(a=1,b=2,c=3) == {\'a\':1, \'b\':2, \'c\':3}</pre>\r\n<p>其实python中就带有dict类，使用dict(a=1,b=2,c=3)即可创建一个字典了。</p>','',1,0,'2011-05-19 15:05:00','2011-05-19 15:44:32',2,'python',0),(4,'Ubuntu下安装PIL','install-pil',1,'<p>环境：ubuntu11.04<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Python2.7</p>\r\n<p>1. 从<a href=\"http://www.pythonware.com/products/pil/\">http://www.pythonware.com/products/pil/</a>下载PIL，现在最新的为PIL 1.1.7<br />&nbsp; &nbsp; 下载后解压 tar zxf Imaging-1.1.7.tar.gz.&nbsp;</p>\r\n<p>2. 编译 python setup.py build 出现错误 <span style=\"color: #ff0000;\">Python.h: 没有那个文件或目录</span>，需要下载python-dev, 执行sudo apt-get install python-dev, 它会自动匹配你电脑上安装的Python版本进行安装例如python2.7-dev</p>\r\n<p>&nbsp; &nbsp; 安装完后再次执行python setup.py build, 会出现一大串信息，但在信息的最后显示如下内容</p>\r\n<pre class=\"prettyprint\">To check the build, run the selftest.py script.\r\n--------------------------------------------------------------------\r\nPIL 1.1.7 TEST SUMMARY \r\n--------------------------------------------------------------------\r\nPython modules loaded from ./PIL\r\nBinary modules loaded from ./PIL\r\n--------------------------------------------------------------------\r\n--- PIL CORE support ok\r\n*** TKINTER support not installed\r\n*** JPEG support not installed\r\n*** ZLIB (PNG/ZIP) support not installed\r\n*** FREETYPE2 support not installed\r\n*** LITTLECMS support not installed\r\n--------------------------------------------------------------------\r\nRunning selftest:\r\n*****************************************************************\r\nFailure in example:\r\ntry:\r\n _info(Image.open(os.path.join(ROOT, \"Images/lena.jpg\")))\r\nexcept IOError, v:\r\n print v\r\nfrom line #24 of selftest.testimage\r\nExpected: (\'JPEG\', \'RGB\', (128, 128))\r\nGot: decoder jpeg not available\r\n1 items had failures:\r\n   1 of  57 in selftest.testimage\r\n***Test Failed*** 1 failures.\r\n*** 1 tests of 57 failed.</pre>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 337px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&ldquo;decoder jpeg not available&rdquo;说明你的系统没有安装Jpeg支持，需要去http://www.ijg.org下载jpeg的支持库, 安装之, &nbsp;保留解压过的安装包(假设为/usr/local/lib/jpeg-8c).</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 337px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">有解压后的PIL文件夹中找到setup.py文件，打开并修改JPEG_ROOT=\"/usr/local/lib/jpeg-8c\"</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 337px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">再执行python setup.py build， 成功</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 337px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">最后 sudo python setup.py install</div>\r\n<p>&ldquo;<span style=\"color: #ff0000;\">decoder jpeg not available</span>&rdquo;说明你的系统没有安装Jpeg支持，需要去<a href=\"http://www.ijg.org\">http://www.ijg.org</a>下载jpeg的支持库, 安装之, &nbsp;保留解压过的安装包(假设为/usr/local/lib/jpeg-8c).<br />在解压后的PIL文件夹中找到setup.py文件，打开并修改JPEG_ROOT=\"/usr/local/lib/jpeg-8c\"再执行python setup.py build， 成功</p>\r\n<p>3. 最后 sudo python setup.py install，安装成功！</p>','',1,1,'2011-05-21 20:03:27','2011-05-21 20:13:31',2,'python, PIL, ubuntu',3),(5,'Ubuntu下安装MySQLdb','install-mysqldb',1,'<p>到http://sourceforge.net/projects/mysql-python/files/mysql-python/下载最新的版本，当前最新版为MySQL-python-1.2.3.tar.gz</p>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">1） 解压后执行python setup.py build 出现错误&ldquo;fatal error: Python.h: 没有那个文件或目录&rdquo;</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">这时需要安装Python-dev， 执行命令sudo apt-get install python-dev</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp;再次执行python setup.py build， 出现错误：ImportError: No module named setuptools需要安装setuptools，到http://pypi.python.org/pypi/setuptools下载一个与自己电脑上安装的Python版本相应的setuptool</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp;再次执行python setup.py build， 出现错误：EnvironmentError: mysql_config not found，需要执行命令sudo apt-get install libmysqlclient-dev解决。</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp;再次执行python setup.py build， 终于OK了，晕啊！！！</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">最后sudo python setup.py install</div>\r\n<p>解压后执行python setup.py build 出现错误<span style=\"color: #ff0000;\">&ldquo;fatal error: Python.h: 没有那个文件或目录&rdquo;<span style=\"color: #000000;\">,</span>&nbsp;</span>这时需要安装Python-dev， 执行命令</p>\r\n<pre class=\"prettyprint\">sudo apt-get install python-dev<span style=\"white-space: normal;\">&nbsp;&nbsp;</span></pre>\r\n<p>再次执行python setup.py build， 出现错误<span style=\"color: #ff0000;\">\"ImportError: No module named setuptools\"<span style=\"color: #000000;\">,</span>&nbsp;</span>需要安装setuptools，到http://pypi.python.org/pypi/setuptools下载一个与自己电脑上安装的Python版本相应的setuptool</p>\r\n<p>再次执行python setup.py build， 出现错误<span style=\"color: #ff0000;\">\"EnvironmentError: mysql_config not found\"</span>, 需要执行命令</p>\r\n<pre class=\"prettyprint\">sudo apt-get install libmysqlclient-dev</pre>\r\n<p>再再次执行python setup.py build， 终于OK了，晕啊！！！</p>\r\n<p>最后sudo python setup.py install</p>\r\n<p>为了验证MySQLdb安装是否成功安装，在命令行输入Python, 并导入MySQLdb</p>\r\n<pre class=\"prettyprint\">senbz@Senbonzakura:/usr/local/lib$ python\r\nPython 2.7.1+ (r271:86832, Apr 11 2011, 18:05:24) \r\n[GCC 4.5.2] on linux2\r\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\r\n&gt;&gt;&gt; import MySQLdb\r\nTraceback (most recent call last):\r\n  File \"&lt;stdin&gt;\", line 1, in &lt;module&gt;\r\nImportError: No module named MySQLdb</pre>\r\n<p>发现找不到MySQLdb，这下火了，于是到Python的安装目录/usr/local/lib/python2.7下去看看到底没有MySQL_python这个文件，发现这个的确在dist-packages下，但是右上角有个&ldquo;X&rdquo;号，原来是不可读的，于是为该文件增加可读属性，搞定！</p>','',1,1,'2011-05-21 20:22:47','2011-05-21 20:22:47',2,'python, mysql',4),(6,'Ubuntu下更改Mysql默认字符集','mysql-character',1,'<p>在ubuntu安装Mysql Server比较简单,一条命令就可以搞定,如下:</p>\r\n<pre class=\"prettyprint\">sudo apt-get install mysql-server</pre>\r\n<p>但是用apt-get安装mysql不能设置默认字符集,可能是我不明白怎么弄,安装完默认字符集是latin1,可以在登录mysql后用status命令查看,显示内容如下:</p>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Connection id:<span style=\"white-space: pre;\"> </span>85</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Current database:<span style=\"white-space: pre;\"> </span></div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Current user:<span style=\"white-space: pre;\"> </span>root@localhost</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">SSL:<span style=\"white-space: pre;\"> </span>Not in use</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Current pager:<span style=\"white-space: pre;\"> </span>stdout</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Using outfile:<span style=\"white-space: pre;\"> </span>\'\'</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Using delimiter:<span style=\"white-space: pre;\"> </span>;</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Server version:<span style=\"white-space: pre;\"> </span>5.1.54-1ubuntu4 (Ubuntu)</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Protocol version:<span style=\"white-space: pre;\"> </span>10</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Connection:<span style=\"white-space: pre;\"> </span>Localhost via UNIX socket</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Server characterset:<span style=\"white-space: pre;\"> </span>utf8</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Db &nbsp; &nbsp; characterset:<span style=\"white-space: pre;\"> </span>utf8</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Client characterset:<span style=\"white-space: pre;\"> </span>utf8</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Conn. &nbsp;characterset:<span style=\"white-space: pre;\"> </span>utf8</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">UNIX socket:<span style=\"white-space: pre;\"> </span>/var/run/mysqld/mysqld.sock</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">Uptime:<span style=\"white-space: pre;\"> </span>51 min 27 sec</div>\r\n<pre class=\"prettyprint\">Connection id:		85\r\nCurrent database:	\r\nCurrent user:		root@localhost\r\nSSL:			Not in use\r\nCurrent pager:		stdout\r\nUsing outfile:		\'\'\r\nUsing delimiter:	;\r\nServer version:		5.1.54-1ubuntu4 (Ubuntu)\r\nProtocol version:	10\r\nConnection:		Localhost via UNIX socket\r\nServer characterset:	latin1\r\nDb     characterset:	latin1\r\nClient characterset:	latin1\r\nConn.  characterset:	latin1\r\nUNIX socket:		/var/run/mysqld/mysqld.sock\r\nUptime:			51 min 27 sec</pre>\r\n<p><span style=\"color: #555555; font-family: Arial, Helvetica, sans-serif; line-height: 22px;\">解决办法就是更改/etc/mysql/my.cnf文件,在[mysqld]和[mysql]下添加如下内容:</span></p>\r\n<pre class=\"prettyprint\">[mysqld]\r\ndefault-character-set=utf8\r\ndefault-storage-engine=INNODB\r\n　\r\n[mysql]\r\ndefault-character-set=utf8</pre>\r\n<p>然后重启mysql server即可,命令如下:</p>\r\n<pre class=\"prettyprint\">sudo /etc/init.d/mysql　restart</pre>\r\n<p>然后再查看一下status,呵呵!字符集变为utf-8了!</p>','',1,1,'2011-05-21 20:32:32','2011-05-21 20:32:32',13,'mysql, ubuntu',3),(7,'Ubuntu下开机自动挂载NTFS格式分区','auto-mount-ntfs-disk',1,'<p>执行以下命令：</p>\r\n<pre class=\"prettyprint\">sudo apt-get install ntfs-config ntfs-3g</pre>\r\n<p>然后在打开&ldquo;系统-&gt;系统管理-&gt;NTFS写入支持配置程序&rdquo;然后选中&ldquo;启用内部设备写支持&rdquo;和&ldquo;启用外部设备写支持&rdquo;两项，重启后就可以自动挂载NTFS分区</p>\r\n<p>但是我在装完后发现&ldquo;NTFS写入支持配置程序&rdquo;无法打开，然后执行命令：sudo ntfs-config打开，但是出现了错误</p>\r\n<pre class=\"prettyprint\">Traceback (most recent call last):\r\nFile \"/usr/bin/ntfs-config\", line 102, in &lt;module&gt;\r\nmain(args, opts)\r\nFile \"/usr/bin/ntfs-config\", line 75, in main\r\napp = NtfsConfig()\r\nFile \"/usr/lib/pymodules/python2.6/NtfsConfig/NtfsConfig.py\", line 56, in __init__\r\nos.mkdir(HAL_CONFIG_DIR)\r\nOSError: [Errno 2] 没有那个文件或目录: \'/etc/hal/fdi/policy</pre>\r\n<p>解决办法如下：</p>\r\n<p>打开&ldquo; /usr/lib/pymodules/python2.6/NtfsConfig/NtfsConfig.py &rdquo;文件，修改56行，即将&ldquo;os.mkdir(HAL_CONFIG_DIR)&rdquo;改为&ldquo;os.makedirs(HAL_CONFIG_DIR)&rdquo;。然后以根用户权限运行ntfs-config。</p>\r\n<p><span style=\"color: #ff0000;\">如何取消挂载的分区</span></p>\r\n<p>在自动挂载完分区后，发现无法取消，此时应该手动编辑/etc/fstab文件，执行如下命令</p>\r\n<pre class=\"prettyprint\">sudo gedit /etc/fstab</pre>\r\n<p>把不需要挂载分区的那行注释掉或者直接删除掉</p>','',1,1,'2011-05-21 21:25:19','2011-05-21 21:35:53',6,'ubuntu',3),(8,'无法安装SQLServer2008 ','cannot-install-sqlserver2008',1,'<div style=\"color: #000000; font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #ffffff; margin: 8px;\">\r\n<div style=\"color: #000000; font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px; background-image: initial; background-attachment: initial; background-origin: initial; background-clip: initial; background-color: #ffffff; margin: 8px;\">\r\n<p>在安装SQLServer2008时出现：&ldquo;The SQL Server 2005 Express Tools are installed. To continue, remove the SQL Server 2005 Express Tools&rdquo;的提示，可按下列方法解决</p>\r\n<p>1）在&ldquo;添加删除程序&rdquo;里删除Microsoft SQL Server Management Studio Express</p>\r\n<p>2）如果你找不到Microsoft SQL Server Management Studio Express，可以通过修改注册表</p>\r\n<p>&nbsp; &nbsp; HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Microsoft SQL Server\\90\\Tools\\<span style=\"color: #ff0000;\">ShellSEM</span></p>\r\n<p>并删除<span style=\"color: #ff0000;\">ShellSEM</span>项即可！</p>\r\n</div>\r\n</div>','',1,1,'2011-05-23 10:51:18','2011-05-23 17:36:52',12,'sqlserver',2),(9,'可空类型System.Nullable的反射','reflection-for-nullable',1,'<p>代码如下：把从数据库中查询得到的 List 转换为 DataTable</p>\r\n<pre class=\"prettyprint\">/// &lt;summary&gt;\r\n/// ListをDataTableにキャストする\r\n/// &lt;/summary&gt;\r\n/// &lt;typeparam name=\"T\"&gt;&lt;/typeparam&gt;\r\n/// &lt;param name=\"lstSource\"&gt;&lt;/param&gt;\r\n/// &lt;returns&gt;&lt;/returns&gt;\r\npublic static DataTable Serialize&lt;T&gt;(List&lt;T&gt; lstSource)\r\n{\r\n    DataTable dtSerialize = new DataTable();\r\n\r\n    // column names \r\n    PropertyInfo[] oProps = null;\r\n\r\n    if (lstSource == null) return dtSerialize;\r\n\r\n    foreach (T rec in lstSource)\r\n    {\r\n        if (oProps == null)\r\n        {\r\n            oProps = ((Type)rec.GetType()).GetProperties();\r\n            foreach (PropertyInfo pi in oProps)\r\n            {\r\n                Type colType = pi.PropertyType;\r\n\r\n                if ((colType.IsGenericType) &amp;&amp; (colType.GetGenericTypeDefinition()== typeof(Nullable&lt;&gt;)))\r\n                {   //判断是否为 System.Nullable 类型\r\n                    colType = colType.GetGenericArguments()[0];\r\n                }\r\n                dtSerialize.Columns.Add(new DataColumn(pi.Name, colType));\r\n            }\r\n        }\r\n\r\n        DataRow dr = dtSerialize.NewRow();\r\n        foreach (PropertyInfo pi in oProps)\r\n        {\r\n            dr[pi.Name] = (pi.GetValue(rec, null) == null ? DBNull.Value : pi.GetValue(rec, null));\r\n        }\r\n        dtSerialize.Rows.Add(dr);\r\n    }\r\n    return dtSerialize;\r\n}</pre>\r\n<p>&nbsp;</p>\r\n<p>&nbsp;</p>','',1,1,'2011-05-26 17:10:35','2011-05-26 17:10:35',7,'asp.net',3),(10,'Javascript给数字添加千位符','javascript-thousand-separators',1,'<p>如1234567 则显示为 1,234,567.00</p>\r\n<pre class=\"prettyprint\">function   formatNumber(eValue)\r\n{  \r\n    var   intPart   =   \'\';  \r\n    var   decPart   =   \'\';  \r\n    if   (eValue.indexOf(\',\')   &gt;=   0)\r\n    {  \r\n          eValue=eValue.replace(/,/g,\'\');  \r\n    }  \r\n    //判断是否包含\'.\'  \r\n    if   (eValue.indexOf(\'.\')&gt;=0)\r\n    {  \r\n          intPart   =   eValue.split(\'.\')[0];  \r\n          decPart   =   eValue.split(\'.\')[1];  \r\n    }else\r\n    {  \r\n          intPart   =   eValue;  \r\n    }  \r\n    var   num     =     intPart   +   \'\';      \r\n    var   re   =   /(-?\\d+)(\\d{3})/      \r\n    while(re.test(num))\r\n    {      \r\n          num   =   num.replace(re,   \'$1,$2\')      \r\n    }      \r\n    if   (eValue.indexOf(\".\")   &gt;=   0)\r\n    {  \r\n          eValue   =   num   +   \".\"   +   decPart;  \r\n    }\r\n    else\r\n    {  \r\n          eValue   =   num + \'.00\';  \r\n    }  \r\n    return   eValue;  \r\n}</pre>','',1,1,'2011-05-26 17:25:14','2011-05-26 17:25:14',8,'javascript',1),(11,'offsetLet,offsetTop,scrollLeft,scrollTop等','offsetLet-offsetTop-scrollLeft-scrollTop',1,'<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">1. offsetTop :</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; 当前对象到其上级层顶部的距离.</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; 不能对其进行赋值.设置对象到页面顶部的距离请用style.top属性.</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; 类型：int。状态：只读。单位：px。</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">2. offsetLeft :</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; 当前对象到其上级层左边的距离.</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; 不能对其进行赋值.设置对象到页面左部的距离请用style.left属性.</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; 类型：int。状态：只读。单位：px。</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">3. offsetWidth :</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; 当前对象的宽度.</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; 与style.width属性的区别在于:如对象的宽度设定值为百分比宽度,则无论页面变大还是变小,style.width都返回此百分比,而offsetWidth则返回在不同页面中对象的宽度值而不是百分比值</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">4. offsetHeight :</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; 与style.height属性的区别在于:如对象的宽度设定值为百分比高度,则无论页面变大还是变小,style.height都返回此百分比,而offsetHeight则返回在不同页面中对象的高度值而不是百分比值</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">5. offsetParent :</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; 当前对象的上级层对象.</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; 注意.如果对象是包括在一个DIV中时,此DIV不会被当做是此对象的上级层,(即对象的上级层会跳过DIV对象)上级层是Table时则不会有问题。利用这个属性，可以得到当前对象在不同大小的页面中的绝对位置．</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; 类型：Node。状态：只读。</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">6. scrollLeft :</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; 对象的最左边到对象在当前窗口显示的范围内的左边的距离．</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; 即是在出现了横向滚动条的情况下，滚动条拉动的距离．</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">7. scrollTop</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; 对象的最顶部到对象在当前窗口显示的范围内的顶边的距离．</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; 即是在出现了纵向滚动条的情况下，滚动条拉动的距离．</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">8. scrollHeight, scrollWidth&nbsp;</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; 返回元素的完整的高度和宽度，以像素为单位。当一个元素拥有滚动条时（比如由于 CSS 的 overflow 属性），这些属性和 offsetHeight 与 offsetWidth 不同，offsetHeight 和 offsetWidth 只是报告元素的可见部分的大小。</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">9. 鼠标位置</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; clientX, clientY是鼠标当前相对于网页的位置，当鼠标位于页面左上角时clientX=0, clientY=0；&nbsp;</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; offsetX, offsetY是鼠标当前相对于网页中的某一区域的位置，当鼠标位于页面中这一区域的左上角时offsetX=0, offsetY=0；&nbsp;</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; screenX, screenY是相对于用户显示器的位置&nbsp;</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">&nbsp; &nbsp; &nbsp; x,y是鼠标当前相对于当前浏览器的位置</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">10. 示例：</div>\r\n<p>1. offsetTop :<br />&nbsp; &nbsp; &nbsp; 当前对象到其上级层顶部的距离.<br />&nbsp; &nbsp; &nbsp; 不能对其进行赋值.设置对象到页面顶部的距离请用style.top属性.<br />&nbsp; &nbsp; &nbsp; 类型：int。状态：只读。单位：px。</p>\r\n<p>2. offsetLeft :<br />&nbsp; &nbsp; &nbsp; 当前对象到其上级层左边的距离.<br />&nbsp; &nbsp; &nbsp; 不能对其进行赋值.设置对象到页面左部的距离请用style.left属性.<br />&nbsp; &nbsp; &nbsp; 类型：int。状态：只读。单位：px。</p>\r\n<p>3. offsetWidth :<br />&nbsp; &nbsp; &nbsp; 当前对象的宽度.<br />&nbsp; &nbsp; &nbsp; 与style.width属性的区别在于:如对象的宽度设定值为百分比宽度,则无论页面变大还是变小,style.width都返回此百分比,而offsetWidth则返回在不同页面中对象的宽度值而不是百分比值</p>\r\n<p>4. offsetHeight :<br />&nbsp; &nbsp; &nbsp; 与style.height属性的区别在于:如对象的宽度设定值为百分比高度,则无论页面变大还是变小,style.height都返回此百分比,而offsetHeight则返回在不同页面中对象的高度值而不是百分比值</p>\r\n<p>5. offsetParent :<br />&nbsp; &nbsp; &nbsp; 当前对象的上级层对象.<br />&nbsp; &nbsp; &nbsp; 注意.如果对象是包括在一个DIV中时,此DIV不会被当做是此对象的上级层,(即对象的上级层会跳过DIV对象)上级层是Table时则不会有问题。利用这个属性，可以得到当前对象在不同大小的页面中的绝对位置．<br />&nbsp; &nbsp; &nbsp; 类型：Node。状态：只读。</p>\r\n<p>6. scrollLeft :<br />&nbsp; &nbsp; &nbsp; 对象的最左边到对象在当前窗口显示的范围内的左边的距离．<br />&nbsp; &nbsp; &nbsp; 即是在出现了横向滚动条的情况下，滚动条拉动的距离．</p>\r\n<p>7. scrollTop<br />&nbsp; &nbsp; &nbsp; 对象的最顶部到对象在当前窗口显示的范围内的顶边的距离．<br />&nbsp; &nbsp; &nbsp; 即是在出现了纵向滚动条的情况下，滚动条拉动的距离．</p>\r\n<p>8. scrollHeight, scrollWidth<br />&nbsp; &nbsp; &nbsp; 返回元素的完整的高度和宽度，以像素为单位。当一个元素拥有滚动条时（比如由于 CSS 的 overflow 属性），这些属性和 offsetHeight 与 offsetWidth 不同，offsetHeight 和 offsetWidth 只是报告元素的可见部分的大小。</p>\r\n<p>9. 鼠标位置<br />&nbsp; &nbsp; &nbsp; clientX, clientY是鼠标当前相对于网页的位置，当鼠标位于页面左上角时clientX=0, clientY=0；<br />&nbsp; &nbsp; &nbsp; offsetX, offsetY是鼠标当前相对于网页中的某一区域的位置，当鼠标位于页面中这一区域的左上角时offsetX=0, offsetY=0；<br />&nbsp; &nbsp; &nbsp; screenX, screenY是相对于用户显示器的位置&nbsp;<br />&nbsp; &nbsp; &nbsp; x,y是鼠标当前相对于当前浏览器的位置</p>\r\n<p>10. 示例：</p>\r\n<pre class=\"prettyprint\">//设置元素的显示位置(类似google的智能下拉框)\r\nfunction SetOffsets()\r\n{\r\n    var inputField = document.getElementById(\"txtSubject\");\r\n    var completeDiv = document.getElementById(\"popup\");\r\n    var end = inputField.offsetWidth;\r\n    var left = CalculateOffset(inputField, \"offsetLeft\");      //取得下拉框相对于页面的左偏移量\r\n    //取得下拉框相对于页面的向上偏移量，为要依附的控件的top加上其高度值\r\n    var top = CalculateOffset(inputField, \"offsetTop\") + inputField.offsetHeight;\r\n    \r\n    completeDiv.style.border = \"1px solid black\";\r\n    completeDiv.style.left = left + \"px\";\r\n    completeDiv.style.top = top + \"px\";\r\n    completeDiv.style.width = end + \"px\";\r\n}\r\n//计算页面 控件 的偏移量(相对于整个页面)\r\n//field： 控件对象； attr： 偏移量的属性，取值(top, left等)\r\nfunction CalculateOffset(field, attr)\r\n{\r\n    var offset = 0;\r\n    while(field)\r\n    {\r\n        offset += field[attr];\r\n        field = field.offsetParent;\r\n    }\r\n    return offset;\r\n}</pre>','',1,1,'2011-05-26 17:30:48','2011-05-26 17:30:48',9,'css',0),(12,'Django上传图片','django-upload-image',1,'<p>Django中上传文件有两个步骤，首先是如何获取到上传的图片的二进制流，然后是如何保存的问题， 本例以一个具有ImageField属性的Model来说明。</p>\r\n<p><strong>1. 获取要上传的图片</strong></p>\r\n<p>&nbsp; &nbsp; Django中获取表单POST过来的数据是通过request.POST，而获取上传的文件则是通过request.FILES[\'file\'](file是文件上传控件中的name属性值)。把数据装进Form中可以通过如下代码：</p>\r\n<pre class=\"prettyprint\">profileform = blogforms.ProfileForm(data, request.FILES\r\n    , instance=user.get_profile(), auto_id=True)</pre>\r\n<p><strong>2. 保存图片</strong></p>\r\n<p>&nbsp; &nbsp; 通过models.ImageField的upload_to属性可以把文件保存到指定的地方，但这里一般都为一个静态的路径，有没有办法可以动态的修改要保存到的目录呢，答案当然是可以的。例如保存时可以获取保存时的日期，把上传的文件保存到以日期命名的目录中，例如：</p>\r\n<pre class=\"prettyprint\">image_url = models.ImageField(upload_to=\'album/%Y-%m/\')</pre>\r\n<p>每次保存图片时，django就会在MEDIA_ROOT/album/中创建一个\'YYYY-MM\'的目录，用来保存图片。</p>\r\n<p>&nbsp; &nbsp; 其实，如何查看django的源代码就会发现，upload_to还可以直接使用函数，在运行时就会调用指定的函数来动态的获取地址。例如要在上传文件时，自动保存到以当前登录用户的名词名命名的目录下。</p>\r\n<pre class=\"prettyprint\">class UserProfile(models.Model):&nbsp;<br />    user = models.OneToOneField(User)&nbsp;<br />    image_url = models.ImageField(upload_to=get_upload_to, blank=True, null=True)\r\n\r\ndef get_upload_to(instance, filename):\r\n    return settings.MEDIA_ROOT + \"/\" + instance.user.username + \"/portrait.jpeg\"</pre>\r\n<p>其实通过分析其源代码就可能更清楚的了解其原理了，在django/db/models/fields/files.py中有如下代码</p>\r\n<pre class=\"prettyprint\">def __init__(self, ..., upload_to=\'\', ...):\r\n    ...\r\n\r\n    self.storage = storage or default_storage\r\n    self.upload_to = upload_to\r\n    if callable(upload_to):\r\n        self.generate_filename = upload_to</pre>\r\n<p>它会判断upload_to是不是可以执行的，如果可以执行，则会覆盖掉默认的generate_filename，在保存时调用。</p>','',1,1,'2011-06-09 11:46:51','2011-06-09 11:46:51',3,'django',3),(13,'IE8下VS2005无法调试','cannot-debug-for-ie8',1,'<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">请修改注册表：HKEY_LOCALMACHINE -&gt; SOFTWARE -&gt; Microsoft -&gt; Internet Explorer -&gt; Main&nbsp;</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">增加dword键TabProcGrowth,值为0就行！</div>\r\n<p>请修改注册表：HKEY_LOCALMACHINE -&gt; SOFTWARE -&gt; Microsoft -&gt; Internet Explorer -&gt; Main&nbsp;增加dword键TabProcGrowth,值为0就行！</p>','',1,1,'2011-06-11 17:25:56','2011-06-11 17:25:56',7,'vs2005',2),(14,'获取sqlserver数据库中所有库、表、字段名的方法','sqlserver-get-all-tables',1,'<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">1.获取所有数据库名:&nbsp;</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">SELECT Name FROM Master..SysDatabases ORDER BY Name&nbsp;</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">2.获取所有表名:&nbsp;</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">SELECT Name FROM DatabaseName..SysObjects Where XType=\'U\' ORDER BY Name&nbsp;</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">XType=\'U\':表示所有用户表;&nbsp;</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">XType=\'S\':表示所有系统表;&nbsp;</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">3.获取所有字段名:&nbsp;</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">SELECT Name FROM SysColumns WHERE id=Object_Id(\'TableName\')</div>\r\n<p>1.获取所有数据库名:&nbsp;SELECT Name FROM Master..SysDatabases ORDER BY Name&nbsp;</p>\r\n<p>2.获取所有表名:&nbsp;SELECT Name FROM DatabaseName..SysObjects Where XType=\'U\' ORDER BY Name&nbsp;<br />&nbsp; XType=\'U\':表示所有用户表;&nbsp;<br />&nbsp; XType=\'S\':表示所有系统表;&nbsp;</p>\r\n<p>3.获取所有字段名:&nbsp;SELECT Name FROM SysColumns WHERE id=Object_Id(\'TableName\')</p>','',1,1,'2011-06-11 17:30:42','2011-06-11 17:30:42',12,'sqlserver',3),(15,'SQL Server中多行多列连接成为单行单列','sqlserver-row-to-column',1,'<p><span style=\"color: #0000ff;\">1. 程序效果</span></p>\r\n<p>&nbsp; &nbsp; 原始结构：<br />&nbsp; &nbsp; Column1 Column2&nbsp;<br />&nbsp; &nbsp; --------- ---------<br />&nbsp; &nbsp; 1 A&nbsp;<br />&nbsp; &nbsp; 1 B<br />&nbsp; &nbsp; 2 C<br />&nbsp; &nbsp; 2 D<br />&nbsp; &nbsp; 2 E<br />&nbsp; &nbsp; 3 F</p>\r\n<p>&nbsp; &nbsp; 查询效果：&nbsp;<br />&nbsp; &nbsp; Column1 Column2&nbsp;<br />&nbsp; &nbsp; --------- ---------<br />&nbsp; &nbsp; 1 A,B<br />&nbsp; &nbsp; 2 C,D,E<br />&nbsp; &nbsp; 3 F</p>\r\n<p>&nbsp; &nbsp; 即将 Column1 相同的行的 Column2 连成一列。&nbsp;</p>\r\n<p><span style=\"color: #0000ff;\">2. 建立测试数据</span></p>\r\n<p>1) 建表</p>\r\n<pre class=\"prettyprint\">if exists (\r\n    select * from dbo.sysobjects \r\n    where id = object_id(N\'[dbo].[VertToHorzSample]\') \r\n    and OBJECTPROPERTY(id, N\'IsUserTable\') = 1\r\n) \r\n    drop table [dbo].[VertToHorzSample] \r\nGO \r\n-- 建立测试数据 \r\nCREATE TABLE VertToHorzSample( \r\n    Column1 int, \r\n    Column2 varchar(100) \r\n) \r\nGO\r\n\r\nINSERT INTO VertToHorzSample(Column1, Column2) \r\n    SELECT 1, \'A\'       UNION ALL \r\n    SELECT 1, \'B\'       UNION ALL \r\n    SELECT 2, \'C\'       UNION ALL \r\n    SELECT 2, \'D\'       UNION ALL \r\n    SELECT 2, \'E\'       UNION ALL \r\n    SELECT 3, \'F\'</pre>\r\n<p>2) 建立辅助函数</p>\r\n<pre class=\"prettyprint\">if exists (\r\n    select * from dbo.sysobjects \r\n    where id = object_id(N\'[dbo].[ConvertVertToHorz]\') \r\n    and xtype in (N\'FN\', N\'IF\', N\'TF\')\r\n) \r\n    drop function [dbo].[ConvertVertToHorz] \r\nGO \r\n-- 建立辅助函数 \r\nCREATE FUNCTION ConvertVertToHorz(@Col1Val int) \r\n    RETURNS VARCHAR(8000) \r\nAS \r\nBEGIN \r\n    -- 实际项目中，应该考虑 @RetVal 是否会超过 8000 个字符 \r\n    DECLARE @RetVal varchar(8000) \r\n    SET @RetVal = \'\' \r\n    -- 通过递归 SELECT 连接指定列存储到临时变量中 \r\n    SELECT @RetVal = Column2 + \',\' + @RetVal FROM VertToHorzSample WHERE Column1 = @Col1Val \r\n    -- 连接多列 \r\n    -- SELECT @RetVal = Column2 + \',\' + Column3 + \',\' + Column4 + \',\' + @RetVal \r\n    -- FROM VertToHorzSample WHERE Column1 = @Col1Val \r\n    -- 去掉尾巴的 , (逗号) \r\n    IF LEN(@RetVal) &gt; 0 \r\n          SET @RetVal = LEFT(@RetVal, LEN(@RetVal) - 1) \r\n    --PRINT @RetVal \r\n    RETURN @RetVal \r\nEND</pre>\r\n<p><span style=\"color: #0000ff;\">3. 测试结果</span></p>\r\n<pre class=\"prettyprint\">SELECT Column1, dbo.ConvertVertToHorz(Column1) Column2 \r\nFROM (SELECT DISTINCT Column1 FROM VertToHorzSample) t</pre>\r\n<p>Column1 Column2<br />----------- -----------<br />1 A,B<br />2 C,D,E<br />3 F</p>','',1,1,'2011-06-11 19:26:40','2011-06-11 19:26:40',12,'sqlserver',3),(16,'Ubuntu中如何查找软件包','ubuntu-how-to-find-lib',1,'<p>在Linux下要装某软件时，经常会遇到需要安装这样那样的依赖，但苦于不知道软件包的具体名称，无法使用 apt-get 进行安装，这时可以使用如下的命令来查找所需软件包的全名，例如要查找 glew 时：</p>\r\n<pre class=\"prettyprint\">sudo apt-cache search glew</pre>\r\n<p>命令执行会得到如下的信息</p>\r\n<pre class=\"prettyprint\">libglew1.5 - The OpenGL Extension Wrangler - runtime environment\r\nlibglew1.5-dev - The OpenGL Extension Wrangler - development environment\r\nlibglewmx1.5 - The OpenGL Extension Wrangler - runtime environment\r\nlibglewmx1.5-dev - The OpenGL Extension Wrangler - development environment\r\npython-pyglew - GLEW bindings for Python\r\nglew-utils - OpenGL Extension Wrangler - ユーティリティ</pre>\r\n<p>根据返回的信息，就可以选择要安装的包</p>','',1,1,'2011-06-14 20:30:23','2011-06-14 20:36:37',6,'ubuntu',4),(17,'Javascript对话框','javascript-dialog',1,'<p>现在通过 javascript 来实现一个对话框，并可以用鼠标任意的拖动</p>\r\n<pre class=\"prettyprint\">var MoveDiv = function() {};\r\n\r\n// id 为包含整个对话框的容器的ID\r\n// titleID 为可以用鼠标拖动的标题栏的ID\r\nMoveDiv.Move=function(id,titleID)\r\n{\r\n    //整个对话框\r\n    var o =document.getElementById(id);\r\n    //标题栏，可对其移动\r\n    var oTitle =document.getElementById(titleID);\r\n    //在鼠标按下时实现移动\r\n    oTitle.onmousedown = function(e)\r\n    {   \r\n        e = e||window.event;\r\n        o.style.position = \"absolute\";          //一定要设置为absolute\r\n        //获取鼠标相对于对话框的位置\r\n        var x=e.layerX||e.offsetX;\r\n        var y=e.layerY||e.offsetY;\r\n        document.onmousemove = function(e)\r\n        {\r\n            e=e||window.event;\r\n            //鼠标相对于页面边框的位置 - 鼠标相对于对话框的位置 + 页面的滚动位置\r\n            o.style.left=(e.clientX-x+ document.documentElement.scrollLeft)+\"px\";\r\n            o.style.top=(e.clientY-y+ document.documentElement.scrollTop)+\"px\";\r\n            return false ;\r\n        };\r\n\r\n        document.onmouseup=function()\r\n        {\r\n            document.onmousemove=null;\r\n            o.onselectstart = null;\r\n        };\r\n        return false;\r\n    };       \r\n}</pre>\r\n<p>接下来实现HTML代码的部分</p>\r\n<pre class=\"prettyprint\">&lt;div id=\"divDialog\" class=\"dialog\"&gt;\r\n    &lt;table cellpadding=\"0\" cellspacing=\"0\"&gt;\r\n        &lt;!--标题--&gt;\r\n        &lt;tr class=\"popup_title\"&gt;\r\n            &lt;td id=\"popupTitle\" class=\"popup_title1\" \r\n                onmousedown=\"MoveDiv.Move(\'divDialog\',\'popupTitle\')\"&gt;\r\n                编辑\r\n            &lt;/td&gt;\r\n            &lt;td class=\"popup_title2\"&gt;\r\n                &lt;!-- 一个\"X\"符号的关闭图标 --&gt;\r\n            &lt;/td&gt;\r\n        &lt;/tr&gt;\r\n        &lt;tr&gt;\r\n            &lt;td colspan=\"2\"&gt;\r\n                &lt;!-- 对话框的内容 --&gt;\r\n            &lt;/td&gt;\r\n        &lt;/tr&gt;\r\n    &lt;/table&gt;\r\n&lt;/div&gt;</pre>\r\n<p>以上</p>','',1,1,'2011-06-21 14:38:26','2011-06-21 14:38:26',8,'javascript, 对话框',7),(18,'读取MP3文件的ID3v1信息','mp3-id3-info',1,'<p>MP3文件中包含一个叫做ID3的标签，其实有两个标签，一个叫做ID3v1，另外一个叫做ID3v2。这里先介绍ID3v1。</p>\r\n<p>ID3v1结构比较简单，存放在MP3文件的末尾，大家可以用16进制的编辑器（例如：UltraEdit）打开一个MP3文件，注意末尾的128个字节，数据结构定义如下：</p>\r\n<table border=\"0\">\r\n<tbody>\r\n<tr>\r\n<td>名称</td>\r\n<td>位置</td>\r\n<td>长度</td>\r\n<td>内容</td>\r\n</tr>\r\n<tr>\r\n<td>Header</td>\r\n<td>1-3</td>\r\n<td>3</td>\r\n<td>标签头</td>\r\n</tr>\r\n<tr>\r\n<td>Title</td>\r\n<td>4-33</td>\r\n<td>30</td>\r\n<td>标题</td>\r\n</tr>\r\n<tr>\r\n<td>Artist</td>\r\n<td>34-63</td>\r\n<td>30</td>\r\n<td>艺术家</td>\r\n</tr>\r\n<tr>\r\n<td>Album</td>\r\n<td>64-93</td>\r\n<td>30</td>\r\n<td>专辑</td>\r\n</tr>\r\n<tr>\r\n<td>Year</td>\r\n<td>94-97</td>\r\n<td>4</td>\r\n<td>出品年代</td>\r\n</tr>\r\n<tr>\r\n<td>Comment</td>\r\n<td>98-127</td>\r\n<td>30</td>\r\n<td>备注</td>\r\n</tr>\r\n<tr>\r\n<td>Cenre</td>\r\n<td>128</td>\r\n<td>1</td>\r\n<td>类型</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>注意：上述的标签头必须是TAG,否则表示没有标签</p>\r\n<p>ID3v1的各项信息是按顺序依次存放的，每项信息之后没有任何结束标志，如果某项信息长度小于标准长度，使用\"\\0\"来补充。另外Genre是个例外，它用一个字节表示歌曲流派</p>','',1,1,'2011-07-03 20:34:44','2011-07-03 20:34:44',1,'',2),(19,'Ubuntu下编译android2.2.1','ubuntu-compile-android',1,'<p>1. 安装 git 和 gnupg（主要用于获取源码）。</p>\r\n<pre class=\"prettyprint\">$ sudo apt-get install git-core gnupg</pre>\r\n<p>2. 安装其它工具，主要用于编译时需要的依赖</p>\r\n<pre class=\"prettyprint\">$ sudo apt-get install flex bison gperf libsdl-dev libesd0-dev zlib1g-dev \r\n$ sudo apt-get install libwxgtk2.6-dev build-essential zip curl libncurses5-dev</pre>\r\n<p>3. 安装 valgrind 工具</p>\r\n<pre class=\"prettyprint\">$ sudo apt-get install valgrind</pre>\r\n<p>4. 安装 sdk</p>\r\n<p>&nbsp; 如果安装请参考<a href=\"/blog/ywjsailor/article/ubuntu-install-jdk1-5.html\">安装jdk1.5</a>，需要使用jdk1.5主要是用来编译android sdk 的doc。</p>\r\n<p>5. 下载 repo 工具</p>\r\n<pre class=\"prettyprint\">$ curl http://android.git.kernel.org/repo &gt; ～/repo</pre>\r\n<p>6. 导出 android 项目</p>\r\n<pre class=\"prettyprint\">$ mkdir  Android\r\n$ cd  Android\r\n$ repo init -u git://android.git.kernel.org/platform/manifest.git -b froyo</pre>\r\n<p>使用了 -b froyo，此版本对应android2.2，如果不使用此分支，则会下载master上的最新版本的android 3.1</p>\r\n<p>7. 下载 android 源代码</p>\r\n<pre class=\"prettyprint\">$ repo sync</pre>\r\n<p>源码会有2G以上，需要等待很久，如果还需要下载 kernel，请执行</p>\r\n<pre class=\"prettyprint\">$ repo sync kernel</pre>\r\n<p>8. 编译</p>\r\n<p>在源码所在的 Android目录下直接执行 make 命令即可，编译完成后会在源码目录下生成一个OUT目录</p>\r\n<pre class=\"prettyprint\">$ make -j2</pre>\r\n<p>9. 编译SDK</p>\r\n<p>在源码根目录执行 make sdk 命令</p>\r\n<pre class=\"prettyprint\">$ make sdk\r\n$ cd /out/host/linux-x86/sdk/</pre>\r\n<p>可以看到SDK目录下生成了一个&ldquo;android-sdk_eng.yangwanjun_linux-x86&rdquo;文件夹<br />这个文件夹基本包含了Android所有的开发调试工具。该文件夹由以下几部分组成：&nbsp;<br />&ndash;add-ones &ndash;需要增加的新工具路径<br />&ndash;docs &ndash;关于模拟器和各种开发工具的说明等<br />&ndash;platform &ndash;模拟器和开发工具需要的文件<br />&ndash;tools &ndash;模拟器和开发工具的应用程序&nbsp;</p>\r\n<p>10. 启动模拟器</p>\r\n<p>进行到android-sdk_eng.yangwanjun_linux-x86/tools目录，执行./android list</p>\r\n<pre class=\"prettyprint\">$ cd android-sdk_eng.yangwanjun_linux-x86/tools\r\n$ ./android list</pre>\r\n<p>该命令可以查看系统所有已经存在的模拟器</p>\r\n<pre class=\"prettyprint\">$ ./android create avd &ndash;n MyVer2.2 &ndash;t 1</pre>\r\n<p>该命令创建一个名为MyVer2.2的模拟器</p>\r\n<pre class=\"prettyprint\">$ ./emulator -avd MyVer2.2</pre>\r\n<p>该命令启动刚刚创建的模拟器</p>\r\n<p>11. 把源码导入到 Eclipse中</p>\r\n<p>进入到源代码目录中的 development/ide/eclipse/中，把.classpath文件复制到源代码的根目录中，并为该文件添加可写权限</p>\r\n<pre class=\"prettyprint\">$ chmod u+w .classpath</pre>\r\n<p>启动eclipse，新建一个Java Project，选择Create project from existing source，选择android的源码根目录，即刚才把.classpath文件拷进去的那个文件夹，但是导入后发现了3个错误，信息如下：</p>\r\n<pre>project \'ginger\' is missing required library: \r\n\'out/target/common/obj/JAVA_LIBRARIES/google-common-intermediates/javalib.jar \r\nproject \'ginger\' is missing required library: \r\n\'out/target/common/obj/JAVA_LIBRARIES/gsf-client-intermediates/javalib.jar \r\nThe project cannot be built until build path errors are resolved.</pre>\r\n<p>解决办法如下：在eclispe中右击工程文件名，在弹出来的菜单中选择build path -&gt; configure build path，然后在Libraries中把<br />out/target/common/obj/JAVA_LIBRARIES/gsf-client-intermediates/javalib.jar<br />out/target/common/obj/JAVA_LIBRARIES/google-common-intermediates/javalib.jar<br />这两行移除，添加如下JAR包<br />out/target/common/obj/JAVA_LIBRARIES/android-common_intermediates/javalib.jar&nbsp;</p>\r\n<p>执行完上面的步骤后又出现了两个错误&ldquo;EventLogTags cannot be resolved&rdquo;，直接把出错的地方注释掉就可以了</p>','',1,1,'2011-07-06 22:21:53','2011-07-06 22:21:53',15,'android',30),(20,'安装jdk1.5','ubuntu-install-jdk1-5',1,'<p>在编译Android时，需要jdk1.5，可以在官网(http://java.sun.com/javase/downloads/5u22/jdk)下载SDK</p>\r\n<p>1. 把下载好的jdk-1_5_0_22-linux-i586.bin文件拷贝到/usr/lib/jvm/下，执行</p>\r\n<pre class=\"prettyprint\">sudo chmod u+x /usr/lib/jvm/jdk-1_5_0_22-linux-i586.bin</pre>\r\n<p>2. 在添加完文件的可执行权限后，运行该文件,结束后会在同文件夹下生成一个jdk1.5.0_22文件夹。</p>\r\n<pre class=\"prettyprint\">sudo sh jdk-1_5_0_22-linux-i586.bin</pre>\r\n<p>3. 设置环境变量</p>\r\n<pre class=\"prettyprint\">sudo gedit /etc/environment</pre>\r\n<p>修改environment文件</p>\r\n<pre class=\"prettyprint\">PATH=\"......:/usr/lib/jvm/jdk1.5.0_22/bin\"\r\nCLASSPATH=.:/usr/lib/jvm/jdk1.5.0_22/jre/lib\r\nJAVA_HOME=/usr/lib/jvm/jdk1.5.0_22</pre>\r\n<p>4. 如果系统中还安装有其它的JDK版本，如果某些Linux发行版中可能会自带有openJDK，这时还需要进行如下的操作</p>\r\n<pre class=\"prettyprint\">sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk1.5.0_22/bin/java 300\r\nsudo update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk1.5.0_22/bin/javac 300</pre>\r\n<p>5. 最后，执行如下命令来选择系统默认的JDK</p>\r\n<pre class=\"prettyprint\">update-alternatives --config java</pre>\r\n<p>可以通过命令 java -version来查看所安装的版本</p>','',1,1,'2011-07-06 22:51:49','2011-07-06 22:51:49',16,'',29),(21,'删除某文件夹下的文件','ubuntu-delete-files',1,'<p>例如在使用SVN时，要删除目录及所有子目录下的.svn文件夹，可执行如下命令</p>\r\n<pre class=\"prettyprint\">find -name \'.svn*\' |xargs rm -rf</pre>','',1,1,'2011-07-06 23:05:53','2011-07-06 23:05:53',6,'',9),(22,'ubuntu下部署django站点','ubuntu-deploy-django',1,'<p>1. 环境</p>\r\n<p>&nbsp; ubuntu10.04 + apache2 +_mod_wsgi + mysql</p>\r\n<p>2. 安装apache2</p>\r\n<pre class=\"prettyprint\">sudo apt-get install apache2</pre>\r\n<p>安装完后在浏览器中输入http://localhost或http://127.0.0.1，如果出现 It works! 表示安装成功</p>\r\n<p>3. 准备站点程序文件</p>\r\n<p>apache安装完后，就会生成一个/var/www的目录，该目录即apache默认的目录，相当于IIS下的C:\\inetpub\\wwwroot文件夹，为图方便直接把程序文件&lt;site_dir&gt;放在该目录下，可以省支很多事。</p>\r\n<p>4. 安装wsgi</p>\r\n<pre class=\"prettyprint\">$ sudo apt-get install libapache2-mod-wsgi</pre>\r\n<p>其实还有一个mod_python但它只支持到python2.5，python2.6之后就应该使用mode_wsgi<br />wsgi装完后会在/etc/apache2/mods-enabled 下生成wsgi.conf和wsgi.load的两个链接，表明安装成功。</p>\r\n<p>5. 配置httpd.conf文件</p>\r\n<p>打开/etc/apache2/httpd.conf文件，住其中写入&ldquo;WSGIScriptAlias / /var/www/senbzblog/django.wsgi&rdquo;,第一个&ldquo;/&rdquo;为你要提供服务的URL，后面的路径是你的django.wsgi文件的位置，文件名任意。</p>\r\n<p>6. 配置django.wsgi文件</p>\r\n<p>在第5步中，按照指定的&ldquo;/var/www/&lt;site_dir&gt;/django.wsgi&rdquo;路径建立一个django.wsgi文件，放在与settings.py同目录下并住其中写入如下内容</p>\r\n<pre class=\"prettyprint\">import os\r\nimport sys\r\nsys.path.append(\'/var/www\')\r\nos.environ[\'DJANGO_SETTINGS_MODULE\']=\'senbzblog.settings\'\r\nimport django.core.handlers.wsgi\r\napplication=django.core.handlers.wsgi.WSGIHandler()</pre>\r\n<p>注意&ldquo;sys.path.append(\'/var/www\')&rdquo;括号中的目录为，你站点&lt;site_dir&gt;目录的父目录，&ldquo;senbzblog.settings&rdquo;为站点目录中的settings.py文件</p>\r\n<p>7. 重启服务</p>\r\n<pre class=\"prettyprint\">sudo /etc/init.d/apache2 restart</pre>\r\n<p>8. 以下为安装过程中出现过的错误</p>\r\n<p>在Ubuntu下部署django站点时，出现如下问题</p>\r\n<pre class=\"prettyprint\">ExtractionError: Can\'t extract file(s) to egg cache\r\n\r\nThe following error occurred while trying to extract file(s) to the\r\nPython egg cache:\r\n\r\n[Errno 13] Permission denied: \'/var/www/.python-eggs\'\r\n\r\nThe Python egg cache directory is currently set to:\r\n\r\n /var/www/.python-eggs\r\n\r\nPerhaps your account does not have write access to this directory?\r\nYou can change the cache directory by setting the PYTHON_EGG_CACHE\r\nenvironment variable to point to an accessible directory.</pre>\r\n<p>解决方法：</p>\r\n<p>执行如下命令</p>\r\n<pre class=\"prettyprint\">easy_install -UZ kid</pre>\r\n<p>至于原因是什么不知道，只知道这样做就行了(参考：<a href=\"http://www.cnblogs.com/eyescode/archive/2010/04/12.html\">http://www.cnblogs.com/eyescode/archive/2010/04/12.html</a>)</p>','',1,1,'2011-07-07 19:59:07','2011-07-07 19:59:07',3,'django wsgi',16),(23,'在桌面添加计算机，主文件夹等图标','ubuntu-add-computer-icon',1,'<p>Ubuntu下默认是不会显示计算机，网络，回收站等图标，可以通过在终端或Alt+F2打开命令窗口输入&ldquo;<span style=\"color: #ff0000;\">gconf-editor</span>&rdquo;</p>\r\n<p>找到/app/nautilus/desktop</p>\r\n<p><img style=\"border: 1px solid black;\" src=\"/site_media/ywjsailor/gconf-editor.png\" alt=\"配置编辑器\" width=\"700\" height=\"550\" /></p>\r\n<p>勾选：</p>\r\n<p>computer_icon_visible&nbsp;&nbsp; #在桌面上显示计算机图标<br />home_icon_visible&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #桌面主文件夹图标名<br />network_icon_name &nbsp; &nbsp; &nbsp; #在桌面上显示网络服务器图标(网上邻居）<br />trash_icon_visible &nbsp; &nbsp; &nbsp;#在桌面上显示回收站图标</p>\r\n<p>不要勾选：</p>\r\n<p>volumes_visible&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #在桌面上显示已挂载的卷</p>','',1,1,'2011-07-07 20:31:22','2011-07-07 20:31:22',6,'ubuntu',15),(24,'ADT','adt',1,'<p>http://dl-ssl.google.com/Android/eclipse/</p>\r\n<p>pc10150/site_media/eclipse-jee-galileo-linux-gtk.tar.gz</p>\r\n<p>pc10150/site_media/jdk-1_5_0_22-linux-i586.bin</p>\r\n<p>1、sudo gedit /etc/NetworkManager/nm-system-settings.conf将&rdquo;managed=false&rdquo; 改为&rdquo;managed=true&rdquo;2、sudo service network-manager restartOK，问题已经解决了！</p>\r\n<p><a href=\"/site_media/android-sdk-linux_86.tar.gz\">Android Debug文档</a></p>\r\n<p><a href=\"/site_media/ubuntu-10.04-rc-desktop-i386.iso\">下载Ubuntu10.04</a></p>\r\n<p><a href=\"/site_media/SOE-346.avi\">SOE-346</a></p>','',1,1,'2011-07-08 14:11:00','2011-07-08 14:11:00',15,'',38),(25,'Android 编译单独模块','android-compile-single-module',1,'<p><span style=\"color: #ff0000;\">前提： Android源代码编译成功 </span></p>\r\n<p>1、进入Android源代码目录</p>\r\n<p>2、执行. build/envsetup.sh （[.]后面有空格）//设定Android所需环境变量</p>\r\n<p>3、执行make [模块名] 或者 mmm [模块路径] <br />例如：make Calculator //编译计算器模块 <br /><span style=\"color: #ff0000;\">*注意</span>：不是所有的目录都是一个模块，只有在该目录下有Android.mk文件 或者LOCAL_MODULE或者LOCAL_PACKAGE_NAME存在的 <br />例如：packages/apps/Calculator下有Android.mk文件，Calculator就是一个模块</p>\r\n<p>4、执行make snod //重新生成system.img文件</p>','',1,1,'2011-07-12 11:49:44','2011-07-12 11:49:44',15,'android',17),(26,'Android源码分析--计算器','android-calculator-source-analysis',1,'<p><strong><span style=\"color: #0000ff;\">一：类的功能分析</span></strong></p>\r\n<p>1) Calculator:<br />&nbsp; &nbsp; 计算器启动时显示的第一个Activity. 如果是横屏的它会去读取 res/layout-land/main.xml 文件, 如果是竖屏的它就会去读取 res/layout-port/main.xml 文件. 该文件的功能请参照下面的代码注释:</p>\r\n<pre class=\"prettyprint\">public void onCreate(Bundle state) {\r\n    super.onCreate(state);\r\n\r\n    setContentView(R.layout.main);\r\n\r\n    // 该类为从手机内存中文件calculator.data中读取历史信息\r\n    mPersist = new Persist(this);\r\n    mHistory = mPersist.history;\r\n\r\n    // 获取显示表达式和结果的文本框对象, \r\n    // 他是一个ViewSwitcher的子类, 以实现切换进的动画效果\r\n    mDisplay = (CalculatorDisplay) findViewById(R.id.display);\r\n\r\n    // 该类负责执行算术计算\r\n    mLogic = new Logic(this, mHistory, mDisplay, (Button) findViewById(R.id.equal));\r\n    HistoryAdapter historyAdapter = new HistoryAdapter(this, mHistory, mLogic);\r\n    mHistory.setObserver(historyAdapter);\r\n\r\n    // 设置显示的计算器面板(一个0-9的数字的加减乘除, \r\n    // 另一个面板是更为复杂的计算如sin, cos等)\r\n    // 该对象同样是ViewSwitcher的子类,以实现切换进的动画效果\r\n    mPanelSwitcher = (PanelSwitcher) findViewById(R.id.panelswitch);\r\n    // 指定具体显示哪一个面板\r\n    mPanelSwitcher.setCurrentIndex(state==null ? 0 : state.getInt(STATE_CURRENT_VIEW, 0));\r\n\r\n    // 为Listener设置一些必要的信息, 如执行运算所需的 Logic\r\n    mListener.setHandler(mLogic, mPanelSwitcher);\r\n\r\n    // 设置按键的监听器\r\n    mDisplay.setOnKeyListener(mListener);\r\n\r\n    View view;\r\n    // 设置\"Clear\"按钮的长按事件\r\n    if ((view = findViewById(R.id.del)) != null) {\r\n        view.setOnLongClickListener(mListener);\r\n    }\r\n}</pre>\r\n<p>2) Persist:</p>\r\n<p>&nbsp; &nbsp; 该类为对文件\"calculator.data\"进行操作, 用来保存和读取计算器的历史记录, 它有两个函数:load用来读取,save用来保存.</p>\r\n<pre class=\"prettyprint\">private void load() {\r\n    try {\r\n        // 读取文件\r\n        InputStream is = new BufferedInputStream(mContext.openFileInput(FILE_NAME), 8192);\r\n        DataInputStream in = new DataInputStream(is);\r\n        int version = in.readInt();\r\n        if (version &gt; LAST_VERSION) {\r\n            throw new IOException(\"data version \" + version + \"; expected \" + LAST_VERSION);\r\n        }\r\n        // 所有的历史信息都保存在该对象中\r\n        history = new History(version, in);\r\n        in.close();\r\n    } catch (FileNotFoundException e) {\r\n        Calculator.log(\"\" + e);\r\n    } catch (IOException e) {\r\n        Calculator.log(\"\" + e);\r\n    }\r\n}</pre>\r\n<pre class=\"prettyprint\">public void save() {\r\n    try {\r\n        // 打开输出流,准备向文件中写入数据\r\n        OutputStream os = new BufferedOutputStream(mContext.openFileOutput(FILE_NAME, 0), 8192);\r\n        DataOutputStream out = new DataOutputStream(os);\r\n        out.writeInt(LAST_VERSION);\r\n        // 实际的输出由 history 对象来执行\r\n        history.write(out);\r\n        out.close();\r\n    } catch (IOException e) {\r\n        Calculator.log(\"\" + e);\r\n    } \r\n}</pre>\r\n<p>3) EventListener</p>\r\n<p>&nbsp; &nbsp; 该类主要接收一些按键事件, onKey接收手机上硬键盘的操作,如上下左右方向键等, onClick接收按钮的点击事件.</p>\r\n<pre class=\"prettyprint\">class EventListener implements View.OnKeyListener, \r\n                               View.OnClickListener, \r\n                               View.OnLongClickListener {\r\n    // 所有的计算都在该对象中进行\r\n    Logic mHandler;\r\n    PanelSwitcher mPanelSwitcher;\r\n    \r\n    // 在初始时,都要调用该函数, 用来设置一些计算时必需的信息\r\n    void setHandler(Logic handler, PanelSwitcher panelSwitcher) {\r\n        mHandler = handler;\r\n        mPanelSwitcher = panelSwitcher;\r\n    }\r\n    \r\n    //按钮的点击事件\r\n    //@Override\r\n    public void onClick(View view) {\r\n        int id = view.getId();\r\n        switch (id) {\r\n        case R.id.del:            // 删除输入的信息\r\n            mHandler.onDelete();  // 调用Logic的方法进行删除\r\n            break;\r\n\r\n        case R.id.equal:          // 按下\"=\"号后,对结果进行计算\r\n            mHandler.onEnter();   // 调用Logic的方法进行结果计算\r\n            break;\r\n\r\n        default:          // 按下数字键或其它运算符\r\n            // 判断是否是按钮事件\r\n            if (view instanceof Button) {\r\n                // 获取按钮上的文本, 以显示到文本框中\r\n                String text = ((Button) view).getText().toString();\r\n                if (text.length() &gt;= 2) {\r\n                    // add paren after sin, cos, ln, etc. from buttons\r\n                    text += \'(\';\r\n                }\r\n                // 由Logic对象来显示按下的内容\r\n                mHandler.insert(text);\r\n                if (mPanelSwitcher != null &amp;&amp; \r\n                    mPanelSwitcher.getCurrentIndex() == Calculator.ADVANCED_PANEL) {\r\n                    mPanelSwitcher.moveRight();\r\n                }                    \r\n            }\r\n        }\r\n    }\r\n\r\n    // 长按事件\r\n    //@Override\r\n    public boolean onLongClick(View view) {\r\n        int id = view.getId();\r\n        // 判断是否是按下\"Clear\"键\r\n        if (id == R.id.del) {\r\n            // 由Logic对象来执行Clear操作\r\n            mHandler.onClear();\r\n            return true;\r\n        }\r\n        return false;\r\n    }\r\n    \r\n    private static final char[] EQUAL = {\'=\'};\r\n\r\n    // 键盘上的按键\r\n    //@Override\r\n    public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {\r\n        int action = keyEvent.getAction();\r\n        \r\n        // 按下左右按键时的操作\r\n        if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT ||\r\n            keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {\r\n            boolean eat = mHandler.eatHorizontalMove(keyCode == KeyEvent.KEYCODE_DPAD_LEFT);\r\n            return eat;\r\n        }\r\n\r\n        // 未知按键\r\n        //Work-around for spurious key event from IME, bug #1639445\r\n        if (action == KeyEvent.ACTION_MULTIPLE &amp;&amp; keyCode == KeyEvent.KEYCODE_UNKNOWN) {\r\n            return true; // eat it\r\n        }\r\n\r\n        if (keyEvent.getMatch(EQUAL, keyEvent.getMetaState()) == \'=\') {\r\n            if (action == KeyEvent.ACTION_UP) {\r\n                mHandler.onEnter();\r\n            }\r\n            return true;\r\n        }\r\n\r\n        if (keyCode != KeyEvent.KEYCODE_DPAD_CENTER &amp;&amp;\r\n            keyCode != KeyEvent.KEYCODE_DPAD_UP &amp;&amp;\r\n            keyCode != KeyEvent.KEYCODE_DPAD_DOWN &amp;&amp;\r\n            keyCode != KeyEvent.KEYCODE_ENTER) {\r\n            return false;\r\n        }\r\n\r\n        if (action == KeyEvent.ACTION_UP) {\r\n            switch (keyCode) {                \r\n            case KeyEvent.KEYCODE_ENTER:\r\n            case KeyEvent.KEYCODE_DPAD_CENTER:\r\n                mHandler.onEnter();\r\n                break;\r\n                \r\n            case KeyEvent.KEYCODE_DPAD_UP:\r\n                mHandler.onUp();\r\n                break;\r\n\r\n            case KeyEvent.KEYCODE_DPAD_DOWN:            \r\n                mHandler.onDown();\r\n                break;\r\n            }\r\n        }\r\n        return true;\r\n    }\r\n}</pre>\r\n<p>4) Logic</p>\r\n<p>&nbsp; &nbsp; 在EventListener类中用到最多的就是一个Logic的对象mHandler, 主要逻辑都在该类中实现. 所有的表达式计算、文本框中内容的显示及显示时的动画都是在该类中实现或在该类中调用其它类对象来实现. 其构造函数如下:</p>\r\n<pre class=\"prettyprint\">// history: 历史记录, 在用户按下\"=\"键后, 会在onEnter函数中调用, 把表达式写到历史数据中\r\n// display: Login类需要控制文本框的显示, 如在计算时需要显示结果等\r\n// equalButton: 未用到空上变量\r\nLogic(Context context, History history, CalculatorDisplay display, Button equalButton) {\r\n    mErrorString = context.getResources().getString(R.string.error);\r\n    mHistory = history;\r\n    mDisplay = display;\r\n    mDisplay.setLogic(this);\r\n    mEqualButton = equalButton;\r\n    mEnterString = context.getText(R.string.enter).toString();\r\n\r\n    // 初始时显示上一次的计算式\r\n    clearWithHistory(false);\r\n}</pre>\r\n<p>在EventListener类中按下面板中的数字按键时, 应该在文本框中显示该按键上的文字, 就调用了mHandler.insert(text)方法, 其实现如下:</p>\r\n<pre class=\"prettyprint\">void insert(String delta) {\r\n    // 调用CalculatorDisplay对象来实现具体的显示操作\r\n    mDisplay.insert(delta);\r\n}</pre>\r\n<p>还有其它几个方法, 它们也在EventListener中调用的</p>\r\n<pre class=\"prettyprint\">// 当按下\"Clear\"时,在EventListener中调用, 用来清除输入\r\nvoid onDelete() {\r\n    if (getText().equals(mResult) || mIsError) {\r\n        // 如果文本框中内容为空时点了\"Clear\", 则会做一些初始化的操作\r\n        // 如设置计算结果mResult为空, 清除上一次计算的错误信息等\r\n        clear(false);\r\n    } else {\r\n        // 调用CalculatorDisplay的对象来清除当前字符\r\n        mDisplay.dispatchKeyEvent(new KeyEvent(0, KeyEvent.KEYCODE_DEL));\r\n        mResult = \"\";\r\n    }\r\n}\r\n\r\n// 当长时按下\"Clear\"时, 清除文本框中所有的内容\r\n// 并同步更新 History\r\nvoid onClear() {\r\n    clear(false);\r\n}\r\n\r\n// 按下\"=\"时, 在EventListener中调用, 用来计算结果\r\nvoid onEnter() {\r\n    // 获取文本框中的文本, 即要计算的表达式\r\n    String text = getText();\r\n    if (text.equals(mResult)) {\r\n        clearWithHistory(false); //clear after an Enter on result\r\n    } else {\r\n        // 添加历史信息\r\n        mHistory.enter(text);\r\n        try {\r\n            // 执行计算, 并返回结果到 mResult 中\r\n            mResult = evaluate(text);\r\n        } catch (SyntaxException e) {\r\n            mIsError = true;\r\n            mResult = mErrorString;\r\n        }\r\n        if (text.equals(mResult)) {\r\n            // 如果输入的内容就是计算的结果, 则直接显示上一次的结果\r\n            clearWithHistory(true);\r\n        } else {\r\n            // 显示结果\r\n            setText(mResult);\r\n        }\r\n    }\r\n}\r\n\r\n// 显示历史记录中上一次结果\r\nvoid onUp() {\r\n    String text = getText();\r\n    if (!text.equals(mResult)) {\r\n        mHistory.update(text);\r\n    }\r\n    if (mHistory.moveToPrevious()) {\r\n        mDisplay.setText(mHistory.getText(), CalculatorDisplay.Scroll.DOWN);\r\n    }\r\n}\r\n\r\n// 显示历史记录中下一个结果\r\nvoid onDown() {\r\n    String text = getText();\r\n    if (!text.equals(mResult)) {\r\n        mHistory.update(text);\r\n    }\r\n    if (mHistory.moveToNext()) {\r\n        mDisplay.setText(mHistory.getText(), CalculatorDisplay.Scroll.UP);\r\n    }\r\n}\r\n\r\n// 清除文本框中所有的字符\r\n// scroll是否显示滚动效果\r\nprivate void clear(boolean scroll) {\r\n    mDisplay.setText(\"\", scroll ? CalculatorDisplay.Scroll.UP : CalculatorDisplay.Scroll.NONE);\r\n    cleared();\r\n}\r\n\r\n// 清除结果后初始化\r\nvoid cleared() {\r\n    mResult = \"\";       // 设置结果为空\r\n    mIsError = false;   // 清除上一次的错误信息\r\n    updateHistory();    // 更新历史信息\r\n}\r\n\r\n// 调用 History 对象来更新历史记录\r\nvoid updateHistory() {\r\n    mHistory.update(getText());\r\n}\r\n\r\n// 显示历史记录中的信息, 并同时初始化一些常量\r\n// 如果在计算表达式时,本次的计算结果跟上一次的计算结果一样时就会调用该方法\r\nprivate void clearWithHistory(boolean scroll) {\r\n    mDisplay.setText(mHistory.getText(), \r\n                     scroll ? CalculatorDisplay.Scroll.UP : CalculatorDisplay.Scroll.NONE);\r\n    mResult = \"\";\r\n    mIsError = false;\r\n}\r\n\r\nprivate static final int ROUND_DIGITS = 1;\r\n// 计算表达式, 并返回结果\r\nString evaluate(String input) throws SyntaxException {\r\n    if (input.trim().equals(\"\")) {\r\n        return \"\";\r\n    }\r\n\r\n    // 清除结尾的运算符, 如\"3+4+-\"就会转换为\"3+4\"  \r\n    int size = input.length();\r\n    while (size &gt; 0 &amp;&amp; isOperator(input.charAt(size - 1))) {\r\n        input = input.substring(0, size - 1);\r\n        --size;\r\n    }\r\n\r\n    // 把计算结果保存在result变量中, 具体的执行是通过调用Symbols.eval函数\r\n    // 调用doubleToString把double型的结果转换为字符串\r\n    // mLineLength为最大长度, ROUND_DIGITS为小数位数\r\n    String result = Util.doubleToString(mSymbols.eval(input), mLineLength, ROUND_DIGITS);\r\n    if (result.equals(NAN)) { // treat NaN as Error\r\n        mIsError = true;\r\n        return mErrorString;\r\n    }\r\n    return result.replace(\'-\', MINUS).replace(INFINITY, INFINITY_UNICODE);\r\n}</pre>\r\n<p>以下函数为控制文本框显示的函数</p>\r\n<pre class=\"prettyprint\">// 获取文本框中显示的所有内容\r\nprivate String getText() {\r\n    return mDisplay.getText().toString();\r\n}\r\n\r\n// 在光标位置插入字符\r\nvoid insert(String delta) {\r\n    mDisplay.insert(delta);\r\n}\r\n\r\n// 设置文本框中要显示的内容, 用来显示计算结果\r\nprivate void setText(CharSequence text) {\r\n    mDisplay.setText(text, CalculatorDisplay.Scroll.UP);\r\n}</pre>\r\n<p>5) CalculatorDisplay</p>\r\n<p>&nbsp; &nbsp; 在计算器中需要一个文本框来显示算式及结果, 该文本框需要屏蔽掉软键盘及为了显示时的动画效果, 所以需要定制</p>\r\n<pre class=\"prettyprint\">/**\r\n * Provides vertical scrolling for the input/result EditText.\r\n */\r\nclass CalculatorDisplay extends ViewSwitcher {\r\n    // 可以接受的字符：数字和加减乘除运算符以及小数点\r\n    private static final char[] ACCEPTED_CHARS = \r\n        \"0123456789.+-*/\\u2212\\u00d7\\u00f7()!%^\".toCharArray();\r\n\r\n    // 动画的持续时间\r\n    private static final int ANIM_DURATION = 500;\r\n    // 播放动画时滚动的方向\r\n    enum Scroll { UP, DOWN, NONE }\r\n    \r\n    TranslateAnimation inAnimUp;\r\n    TranslateAnimation outAnimUp;\r\n    TranslateAnimation inAnimDown;\r\n    TranslateAnimation outAnimDown;\r\n\r\n    private Logic mLogic;\r\n    private boolean mComputedLineLength = false;\r\n    \r\n    // 构造函数: attrs在layout的XML文件中设置的一些属性\r\n    // 一定要调用super(context, attrs)方法\r\n    public CalculatorDisplay(Context context, AttributeSet attrs) {\r\n        super(context, attrs);\r\n    }\r\n    \r\n    // 在解析完XML中的结束标签后触发的方法\r\n    @Override\r\n    protected void onFinishInflate() {\r\n        super.onFinishInflate();\r\n        Calculator calc = (Calculator) getContext();\r\n        // 调用Calculator类方法自动调整文本框内的字体大小\r\n        calc.adjustFontSize((TextView)getChildAt(0));\r\n        calc.adjustFontSize((TextView)getChildAt(1));\r\n    }\r\n\r\n    @Override\r\n    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {\r\n        super.onLayout(changed, left, top, right, bottom);\r\n        if (!mComputedLineLength) {\r\n            // 设置最大可接受的长度, 该值会在Logic的evaluate函数中把double型的结果转换为字符串时使用\r\n            mLogic.setLineLength(getNumberFittingDigits((TextView) getCurrentView()));\r\n            mComputedLineLength = true;\r\n        }\r\n    }\r\n\r\n    // compute the maximum number of digits that fit in the\r\n    // calculator display without scrolling.\r\n    private int getNumberFittingDigits(TextView display) {\r\n        int available = display.getWidth()\r\n            - display.getTotalPaddingLeft() - display.getTotalPaddingRight();\r\n        Paint paint = display.getPaint();\r\n        float digitWidth = paint.measureText(\"2222222222\") / 10f;\r\n        return (int) (available / digitWidth);\r\n    }\r\n\r\n    protected void setLogic(Logic logic) {\r\n        mLogic = logic;\r\n        NumberKeyListener calculatorKeyListener =\r\n            new NumberKeyListener() {\r\n                public int getInputType() {\r\n                    // 输入类型为TYPE_NULL时不接受任何软键盘的输入\r\n                    return InputType.TYPE_NULL;\r\n                }\r\n            \r\n                protected char[] getAcceptedChars() {\r\n                    return ACCEPTED_CHARS;\r\n                }\r\n\r\n                public CharSequence filter(CharSequence source, int start, int end,\r\n                                           Spanned dest, int dstart, int dend) {\r\n                    /* the EditText should still accept letters (eg. \'sin\')\r\n                       coming from the on-screen touch buttons, so don\'t filter anything.\r\n                    */\r\n                    return null;\r\n                }\r\n            };\r\n\r\n        Editable.Factory factory = new CalculatorEditable.Factory(logic);\r\n        for (int i = 0; i &lt; 2; ++i) {\r\n            EditText text = (EditText) getChildAt(i);\r\n            // 没有背景\r\n            text.setBackgroundDrawable(null);\r\n            // 设置字符工厂\r\n            text.setEditableFactory(factory);\r\n            // 设置键盘监听\r\n            text.setKeyListener(calculatorKeyListener);\r\n        }\r\n    }\r\n\r\n    @Override\r\n    public void setOnKeyListener(OnKeyListener l) {\r\n        getChildAt(0).setOnKeyListener(l);\r\n        getChildAt(1).setOnKeyListener(l);\r\n    }\r\n\r\n    // 在初始化, 屏幕方向或分辨率改变时调用\r\n    @Override\r\n    protected void onSizeChanged(int w, int h, int oldW, int oldH) {\r\n        inAnimUp = new TranslateAnimation(0, 0, h, 0);\r\n        inAnimUp.setDuration(ANIM_DURATION);\r\n        outAnimUp = new TranslateAnimation(0, 0, 0, -h);\r\n        outAnimUp.setDuration(ANIM_DURATION);\r\n\r\n        inAnimDown = new TranslateAnimation(0, 0, -h, 0);\r\n        inAnimDown.setDuration(ANIM_DURATION);\r\n        outAnimDown = new TranslateAnimation(0, 0, 0, h);\r\n        outAnimDown.setDuration(ANIM_DURATION);\r\n    }\r\n\r\n    // 插入输入的数字和运算符, getSelectionStart返回选择范围内光标的起始位置，  \r\n    // 如果什么也没有选中，那么就是当前光标位置\r\n    void insert(String delta) {\r\n        EditText editor = (EditText) getCurrentView();\r\n        int cursor = editor.getSelectionStart();\r\n        editor.getText().insert(cursor, delta);\r\n    }\r\n\r\n    EditText getEditText() {\r\n        return (EditText) getCurrentView();\r\n    }\r\n        \r\n    Editable getText() {\r\n        EditText text = (EditText) getCurrentView();\r\n        return text.getText();\r\n    }\r\n    \r\n    void setText(CharSequence text, Scroll dir) {\r\n        if (getText().length() == 0) {\r\n            dir = Scroll.NONE;\r\n        }\r\n        \r\n        // 设置文本时, 同时显示动画效果\r\n        if (dir == Scroll.UP) {\r\n            setInAnimation(inAnimUp);\r\n            setOutAnimation(outAnimUp);            \r\n        } else if (dir == Scroll.DOWN) {\r\n            setInAnimation(inAnimDown);\r\n            setOutAnimation(outAnimDown);            \r\n        } else { // Scroll.NONE\r\n            setInAnimation(null);\r\n            setOutAnimation(null);\r\n        }\r\n        \r\n        //设置文本。设置后将选择设置为文本长度，这样光标就在文本的最后\r\n        EditText editText = (EditText) getNextView();\r\n        editText.setText(text);\r\n        //Calculator.log(\"selection to \" + text.length() + \"; \" + text);\r\n        editText.setSelection(text.length());\r\n        showNext();\r\n    }\r\n    \r\n    void setSelection(int i) {\r\n        EditText text = (EditText) getCurrentView();\r\n        text.setSelection(i);\r\n    }\r\n    \r\n    int getSelectionStart() {\r\n        EditText text = (EditText) getCurrentView();\r\n        return text.getSelectionStart();\r\n    }\r\n    \r\n    // 接受焦点\r\n    @Override\r\n    protected void onFocusChanged(boolean gain, int direction, Rect prev) {\r\n        //Calculator.log(\"focus \" + gain + \"; \" + direction + \"; \" + prev);\r\n        if (!gain) {\r\n            requestFocus();\r\n        }\r\n    }\r\n}</pre>\r\n<p>6) CalculatorEditable</p>\r\n<p>&nbsp; &nbsp; 计算器中的显示框会自动过滤掉非法的输入, 例如连续输入多个运算符时后面的输入是不会显示的, 并且在输入\"*\", \"/\"等字符时会自动替换为\"&times;\"和\"&divide;\", CalculatorEditable类就起到了自动过滤的作用. 这里主要是通过扩展android.text的一个SpannableStringBuilder类来实现过滤和替换功能的.</p>\r\n<p>&nbsp; &nbsp; 基本用法是: 重写public SpannableStringBuilder replace(int start, int end, CharSequence tb, int tbstart, int tbend)来对文本进行过滤和替换。</p>\r\n<p>7) ColorButton</p>\r\n<p>&nbsp; &nbsp; 第一个功能的实现比较简单，就是重写onSizeChanged和onTextChanged方法，然后采用measureText进行调整位置。</p>\r\n<p>&nbsp; &nbsp; 第二个功能的实现：重写onTouchEvent，通过event.getAction判断是哪种触摸操作，然后执行相应的操作。这里有一个点击后调用动画效果的功能。而Button的点击事件则是通过实现OnClick方法，这里有个mListener，上面已经提到过了。</p>\r\n<p>&nbsp; &nbsp; 第三个功能是采用重写onDraw来实现的。onDraw是用来在控件上绘制东西的一个方法，首先初始一个画笔，然后定义一些绘制效果，如这里的drawMagicFlame画一个边框。这里的动画效果是采用一个计时器实现的，点击Button后会在Button上画一个蓝色边框，然后逐渐消失。显示和消失采用的是改变alpha值实现。</p>\r\n<p>8) PanelSwitcher</p>\r\n<p>&nbsp; &nbsp; PanelSwitcher继承自FrameLayout, 用来实现手势切换面板的功能.</p>\r\n<pre class=\"prettyprint\">class PanelSwitcher extends FrameLayout {\r\n    private static final int MAJOR_MOVE = 60;     // 最小移动距离,达到此值才移动面板\r\n    private static final int ANIM_DURATION = 400; // 移动动画时间\r\n\r\n    private GestureDetector mGestureDetector;\r\n    private int mCurrentView;                     // 当前面板\r\n    private View mChild, mHistoryView;\r\n    private View mChildren[] = new View[0];\r\n\r\n    private int mWidth;\r\n    private TranslateAnimation inLeft;     // 平移动画，往左移动--进入屏幕\r\n    private TranslateAnimation outLeft;    // 往左移动--移出屏幕\r\n\r\n    private TranslateAnimation inRight;    // 往右移动--进入屏幕\r\n    private TranslateAnimation outRight;   // 往右移动--移除屏幕\r\n\r\n    private static final int NONE  = 1;\r\n    private static final int LEFT  = 2;\r\n    private static final int RIGHT = 3;\r\n    private int mPreviousMove;\r\n\r\n    public PanelSwitcher(Context context, AttributeSet attrs) {\r\n        super(context, attrs);\r\n        mCurrentView = 0;\r\n        //手势监听设置\r\n        mGestureDetector = new GestureDetector(context, \r\n                new GestureDetector.SimpleOnGestureListener() {\r\n            //手势滑动\r\n            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,\r\n                                   float velocityY) {\r\n                int dx = (int) (e2.getX() - e1.getX());\r\n\r\n                // 达到最小移动距离并且X方向的速率大于Y方向的速率时移动面板\r\n                if (Math.abs(dx) &gt; MAJOR_MOVE &amp;&amp; Math.abs(velocityX) &gt; Math.abs(velocityY)) {\r\n                    // 速率大于0代表手势往右，反之往左\r\n                    if (velocityX &gt; 0) {\r\n                        moveRight();\r\n                    } else {\r\n                        moveLeft();\r\n                    }\r\n                    return true;\r\n                } else {\r\n                    return false;\r\n                }\r\n            }\r\n        });\r\n    }\r\n\r\n    // 设置当前应该显示的面板\r\n    void setCurrentIndex(int current) {\r\n        mCurrentView = current;\r\n        updateCurrentView();\r\n    }\r\n\r\n    // 把当前面板设为可见,其它的面板不可见\r\n    private void updateCurrentView() {\r\n        for (int i = mChildren.length-1; i &gt;= 0 ; --i) {\r\n            mChildren[i].setVisibility(i==mCurrentView ? View.VISIBLE : View.GONE);\r\n        }\r\n    }\r\n\r\n    // 面板大小改变时调用\r\n    @Override \r\n    public void onSizeChanged(int w, int h, int oldW, int oldH) {\r\n        mWidth = w;\r\n        inLeft   = new TranslateAnimation(mWidth, 0, 0, 0);\r\n        outLeft  = new TranslateAnimation(0, -mWidth, 0, 0);        \r\n        inRight  = new TranslateAnimation(-mWidth, 0, 0, 0);\r\n        outRight = new TranslateAnimation(0, mWidth, 0, 0);\r\n\r\n        inLeft.setDuration(ANIM_DURATION);\r\n        outLeft.setDuration(ANIM_DURATION);\r\n        inRight.setDuration(ANIM_DURATION);\r\n        outRight.setDuration(ANIM_DURATION);\r\n    }\r\n\r\n    protected void onFinishInflate() {\r\n        int count = getChildCount();\r\n        mChildren = new View[count];\r\n        for (int i = 0; i &lt; count; ++i) {\r\n            mChildren[i] = getChildAt(i);\r\n        }\r\n        updateCurrentView();\r\n    }\r\n\r\n    @Override\r\n    public boolean onTouchEvent(MotionEvent event) {\r\n        mGestureDetector.onTouchEvent(event);\r\n        return true;\r\n    }\r\n\r\n    // 中断触摸事件。返回true的话将会触发CANCEL事件\r\n    @Override\r\n    public boolean onInterceptTouchEvent(MotionEvent event) {\r\n        return mGestureDetector.onTouchEvent(event);\r\n    }\r\n\r\n    // 向左滑动时调用的方法\r\n    void moveLeft() {\r\n        // 条件：还没有到最后一个面板\r\n        if (mCurrentView &lt; mChildren.length - 1 &amp;&amp; mPreviousMove != LEFT) {\r\n            mChildren[mCurrentView+1].setVisibility(View.VISIBLE);\r\n            mChildren[mCurrentView+1].startAnimation(inLeft);\r\n            mChildren[mCurrentView].startAnimation(outLeft);\r\n            mChildren[mCurrentView].setVisibility(View.GONE);\r\n\r\n            mCurrentView++;\r\n            mPreviousMove = LEFT;\r\n        }\r\n    }\r\n\r\n    // 向右滑动时调用的方法\r\n    void moveRight() {\r\n        // 条件：还没有到第一个面板\r\n        if (mCurrentView &gt; 0 &amp;&amp; mPreviousMove != RIGHT) {\r\n            mChildren[mCurrentView-1].setVisibility(View.VISIBLE);\r\n            mChildren[mCurrentView-1].startAnimation(inRight);\r\n            mChildren[mCurrentView].startAnimation(outRight);\r\n            mChildren[mCurrentView].setVisibility(View.GONE);\r\n\r\n            mCurrentView--;\r\n            mPreviousMove = RIGHT;\r\n        }\r\n    }\r\n\r\n    int getCurrentIndex() {\r\n        return mCurrentView;\r\n    }\r\n}</pre>\r\n<p>&nbsp;</p>\r\n<p>&nbsp;</p>','',1,1,'2011-07-13 15:49:54','2011-07-13 15:49:54',15,'android',19),(27,'Windows下部署Django站点','windows-deploy-django',1,'<p>环境： windows7, python2.6, django1.2.3</p>\r\n<p>1) 下载apache2</p>\r\n<p>&nbsp; &nbsp; 地址为 <a href=\"http://mirror.bjtu.edu.cn/apache//httpd/binaries/win32/httpd-2.2.19-win32-x86-no_ssl.msi\">httpd-2.2.19-win32-x86-no_ssl.msi</a></p>\r\n<p>2) 下载 mod_wsgi</p>\r\n<p>&nbsp; &nbsp; 地址为 <a href=\"http://code.google.com/p/modwsgi/\">http://code.google.com/p/modwsgi/</a></p>\r\n<p>3) 准备好要发布的站点文件</p>\r\n<p>例如我的站点文件地址为 D:\\workspace\\senbzblog\\senbzblog，在该文件夹下创建一个名为 django.wsgi 的文件， 内容如下</p>\r\n<pre class=\"prettyprint\">import os\r\nimport sys\r\n\r\nsys.path.append(\'D:/workspace/senbzblog\')\r\nos.environ[\'DJANGO_SETTINGS_MODULE\']=\'senbzblog.settings\'\r\nimport django.core.handlers.wsgi\r\napplication=django.core.handlers.wsgi.WSGIHandler()</pre>\r\n<p>4) 配置 apache</p>\r\n<p>把下载好的mod_wsgi文件mod_wsgi-win32-ap22py26-3.3.so放到Apache2.2\\modules文件夹下</p>\r\n<p>修改httpd.conf文件，添加到mod_wsgi的位置 LoadModule wsgi_module modules/mod_wsgi-win32-ap22py26-3.3.so</p>\r\n<p>为了不破坏原来的httpd.conf文件， 现在该文件夹下创建一个httpd_blog.conf的文件， 所有的设置都在该文件夹下进行，记住在httpd.conf文件中添加&ldquo;Include conf/httpd_blog.conf&rdquo;，让它知道新的配置文件的位置</p>\r\n<p>在新的配置文件中添加如下代码：</p>\r\n<pre class=\"prettyprint\">NameVirtualHost *:80\r\n&lt;VirtualHost *:80&gt;\r\n\r\n    ServerName pc10150\r\n    DocumentRoot \"D:/workspace/senbzblog\"\r\n    &lt;Directory \"D:/workspace/senbzblog\"&gt;\r\n        Order allow,deny\r\n        Allow from all\r\n    &lt;/Directory&gt;\r\n\r\n    WSGIScriptAlias / \"D:/workspace/senbzblog/senbzblog/django.wsgi\"\r\n\r\n    &lt;Directory \"D:/workspace/senbzblog\"&gt;\r\n        Order allow,deny\r\n        Allow from all\r\n    &lt;/Directory&gt;\r\n\r\n&lt;/VirtualHost&gt;</pre>\r\n<p>重新启动apache，再打开网站，发现出错无法打开，可以在&ldquo;Apache2.2\\logs\\error.log&rdquo;文件中找到出错信息</p>\r\n<pre class=\"prettyprint\">[client 127.0.0.1] mod_wsgi (pid=13344): \r\n    Target WSGI script \'D:/workspace/senbzblog/senbzblog/django.wsgi\' \r\n    cannot be loaded as Python module.\r\n[client 127.0.0.1] mod_wsgi (pid=13344): \r\n    Exception occurred processing WSGI script \r\n    \'D:/workspace/senbzblog/senbzblog/django.wsgi\'.\r\nTraceback (most recent call last):\r\n  File \"D:/workspace/senbzblog/senbzblog/django.wsgi\", line 1, in &lt;module&gt;\r\n    \\xef\\xbb\\xbfimport os\r\nImportError: No module named os</pre>\r\n<p>提示说在django.wsgi中找不到os模块， 于是把import os 和 import sys 换了下位置，把sys放在前面os放在后面，发现仍然提示找不到os, 说明sys能找到，如是在import sys后面加上 print sys.path 语句，发现sys.path中只有一个\"C:\\Python26\"的目录，于是在django.wsgi中把所有需要的目录都加上，代码如下：</p>\r\n<pre class=\"prettyprint\">import sys\r\nsys.path.append(\'C:/Python26/Lib\')\r\nsys.path.append(\'C:\\\\Python26\\\\DLLs\')\r\nsys.path.append(\'C:\\\\Python26\\\\lib\\\\plat-win\')\r\nsys.path.append(\'C:\\\\Python26\\\\lib\\\\lib-tk\')\r\nsys.path.append(\'C:\\\\Python26\\\\lib\\\\site-packages\')\r\nsys.path.append(\'C:\\\\Python26\\\\lib\\\\site-packages\\\\PIL\')\r\nsys.path.append(\'C:\\\\Python26\\\\lib\\\\site-packages\\\\gtk-2.0\')\r\nsys.path.append(\'C:\\\\Python26\\\\lib\\\\site-packages\\\\whoosh-2.0.0-py2.6.egg\')\r\nimport os\r\n\r\nsys.path.append(\'D:/workspace/senbzblog\')\r\nos.environ[\'DJANGO_SETTINGS_MODULE\']=\'senbzblog.settings\'\r\nimport django.core.handlers.wsgi\r\napplication=django.core.handlers.wsgi.WSGIHandler()</pre>\r\n<p>最后再次重启 Apache， 打开站点成功！</p>\r\n<p>如果发现本机能打开站点，而局域网的其它机不能访问的话，可能是防火墙的问题，需要为防火墙添加例外或是直接关闭。</p>','',1,1,'2011-08-04 11:26:10','2011-08-04 11:26:10',3,'django, wsgi',8),(28,'iOS控件介绍--PickerView','ios-control-pickerview',1,'<p>iOS中的PickerView用来显示一组列表或多组列表，要实现PickerView需要为其指定要显示的数据即数据源，在iOS中分别为UIPickerViewDelegate和UIPickerViewDataSource，它们共同为UIPickerView提供数据。</p>\r\n<p><span style=\"color: #ff00ff;\">UIPickerViewDelegate</span>为PickerView中的每一行绘制实际的内容，它会在特定位置绘制一个字符串或一个视图。PickerView实际上就是从UIPickerViewDelegate获取数据。</p>\r\n<p><span style=\"color: #ff00ff;\">UIPickerViewDataSource</span>并不像.NET中的数据源一样提供真正的数据，它只是用来确定PickerView中的显示的列表的个数和每个列表中的行数。它的工作原理与UIPickerViewDelegate类似。它们会在预先指定的时刻被调用，如果未指定数据源和委托，UIPickerView就无法工作，甚至无法绘制选取器。</p>\r\n<p>很多情况下，UIPickerViewDataSource和UIPickerViewDelegate是同一个对象，通常为UIPickerView的容器视图的视图控制器。</p>\r\n<p>现在通过一个例子来说明如何使用UIPickerView，要实现的功能为从资源文件夹Resources的一个plist文件（属性列表）中获取二维数据显示在PickerView中，PickerView中第一列为一组父元素，第二列为选择第一列中某项后对应的子元素</p>\r\n<p><span style=\"color: #0000ff;\">DependentComponentPIckerViewController.h </span>文件的内容：</p>\r\n<pre class=\"prettyprint\">#import &lt;UIKit/UIKit.h&gt;\r\n#define kParentComponent 0\r\n#define kChildComponent 1\r\n\r\n// 必须实现UIPickerViewDelegate和UIPickerViewDataSource\r\n@interface DependentComponentPIckerViewController : UIViewController \r\n    &lt;UIPickerViewDelegate, UIPickerViewDataSource&gt;\r\n{\r\n    UIPickerView *picker;\r\n    NSDictionary *items;\r\n    NSArray *parents;\r\n    NSArray *children;\r\n}\r\n// 用来关联View容器中的UIPickerView控件\r\n@property(nonatomic, retain)IBOutlet UIPickerView *picker;\r\n// 用来保存从plist文件中取出的数据\r\n@property(nonatomic, retain)NSDictionary *items;\r\n// UIPickerView中第一列\r\n@property(nonatomic, retain)NSArray *parents;\r\n// UIPickerView中的第二列显示的内容\r\n@property(nonatomic, retain)NSArray *children;\r\n@end</pre>\r\n<p><span style=\"color: #0000ff;\">DependentComponentPIckerViewController.m&nbsp;</span>文件的内容：</p>\r\n<pre class=\"prettyprint\">#import \"DependentComponentPIckerViewController.h\"\r\n\r\n@implementation DependentComponentPIckerViewController\r\n@synthesize picker;\r\n@synthesize items;\r\n@synthesize parents;\r\n@synthesize children;\r\n\r\n// 初始化界面，每次进入都会执行\r\n- (void)viewDidLoad \r\n{\r\n    // 从Resources中加载plist文件 items.plist\r\n    NSBundle *bundle = [NSBundle mainBundle];\r\n    NSString *plistPath = [bundle pathForResource:@\"items\"\r\n                                           ofType:@\"plist\"];\r\n    // 把items.plist中的内容加载到 NSDictionary 中\r\n    NSDictionary *dictionary = [[NSDictionary alloc]\r\n                                initWithContentsOfFile:plistPath];\r\n    self.items = dictionary;\r\n    [dictionary release];\r\n\r\n    // 取出NSDictionary中获取所有键的列表并存放到第一个数组中\r\n    // 在分配数组之前，对其中了值按字母进行排序。\r\n    NSArray *component = [self.items allKeys];\r\n    NSArray *sorted = [component \r\n                       sortedArrayUsingSelector:@selector(compare:)];\r\n    self.parents = sorted;\r\n    // 取出第一个数组中的第一项并取出第一项对应的所有子项存放到第二个数组中\r\n    NSString *selectedState = [self.parents objectAtIndex:0];\r\n    NSArray *array = [items objectForKey:selectedState];\r\n    self.children = array;\r\n}\r\n\r\n- (void)didReceiveMemoryWarning \r\n{\r\n    // Releases the view if it doesn\'t have a superview.\r\n    [super didReceiveMemoryWarning];\r\n}\r\n\r\n- (void)viewDidUnload \r\n{\r\n    self.picker = nil;\r\n    self.items = nil;\r\n    self.parents = nil;\r\n    self.children = nil;\r\n    [super viewDidUnload];\r\n    // Release any retained subviews of the main view.\r\n    // e.g. self.myOutlet = nil;\r\n}\r\n\r\n- (void)dealloc \r\n{\r\n    [picker release];\r\n    [items release];\r\n    [parents release];\r\n    [children release];\r\n    [super dealloc];\r\n}\r\n\r\n#pragma mark -\r\n#pragma mark Picker Data Source Methods\r\n// 此处为 UIPickerViewDelegate 为的实现\r\n// 该方法返回一个数字，表示UIPickerView显示有多少列\r\n-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView\r\n{\r\n    return 2;\r\n}\r\n\r\n// 该方法返回数字，表示每一列显示的行数。\r\n-(NSInteger)pickerView:(UIPickerView *)pickerView \r\nnumberOfRowsInComponent:(NSInteger)component\r\n{\r\n    // 通过component变量来区分显示的是哪一列\r\n    if (component == kParentComponent) \r\n    {\r\n        return [self.parents count];\r\n    }\r\n    return [self.children count];\r\n}\r\n\r\n#pragma mark Picker Delegate Methods\r\n// 该方法是定义PickerView的各列中每一行显示的文字\r\n-(NSString *)pickerView:(UIPickerView *)pickerView \r\n            titleForRow:(NSInteger)row\r\n           forComponent:(NSInteger)component\r\n{\r\n    // row 是行索引\r\n    if (component == kParentComponent) \r\n    {\r\n        return [self.parents objectAtIndex:row];\r\n    }\r\n    return [self.children objectAtIndex:row];\r\n}\r\n\r\n// 该方法很重要，为选择某一行是触发的方法。\r\n// 就是此方法显示第二列的内容\r\n-(void)pickerView:(UIPickerView *)pickerView \r\n     didSelectRow:(NSInteger)row\r\n      inComponent:(NSInteger)component\r\n{\r\n    // 如果选择的是第一列的内容\r\n    if (component == kParentComponent) \r\n    {\r\n        NSString *selectedState = [self.parents objectAtIndex:row];\r\n        NSArray *array = [items objectForKey:selectedState];\r\n        self.children = array;\r\n        [picker selectRow:0 inComponent:kChildComponent animated:YES];\r\n        [picker reloadComponent:kChildComponent];\r\n    }\r\n}\r\n\r\n// 定义每一列的宽度\r\n-(CGFloat)pickerView:(UIPickerView *)pickerView \r\n   widthForComponent:(NSInteger)component\r\n{\r\n    if (component == kChildComponent) \r\n    {\r\n        return 90;\r\n    }\r\n    return 200;\r\n}\r\n\r\n@end</pre>\r\n<p>在 viewDidLoad方法中，所做的第一件事就是提供对应用程序Main bundle的引用，</p>\r\n<pre class=\"prettyprint\">NSBundle *bundle = [NSBundle mainBundle];</pre>\r\n<p>那什么是bundle呢，bundle是一种特定的文件类型，其中的内容遵循特定的结构。应用程序和框架都是bundle，此调用返回的bundle对象就表示我们的应用程序。NSBundle的一个主要作用是获取添加到项目的 Resources 文件夹中的资源。在构建应用程序时，这些文件将被复制到应用程序的 Bundle 中。如果需要在代码使用这些资源，则通常需要使用NSBundle。这里使用了 NSBundle 来获取需要的资源路径。</p>','',1,1,'2011-08-11 16:09:21','2011-08-11 16:09:21',17,'iOS',2),(29,'iOS控件介绍--TableView','ios-control-tableview',1,'<p>iOS中表视图是显示表数据的视图对象， 它是 UITableView 类的一个实例。表中的每个可见行都由 UITableView 类实现。表视图是显示表中可见部分的对象，表视图单元负责显示表中的一行</p>\r\n<p>表视图并不负责存储表中的数据。它们只存储足够绘制当前可见行的数据。表视图遵循从 UITableViewDelegate 协议的对象获取配置数据，从遵循 UITableViewDataSource 协议的对象获得行数据。</p>\r\n<p><strong><span style=\"color: #0000ff;\">一. 实现一个简单的表视图</span></strong></p>\r\n<p>如何实现一个 TableView 呢，首先需要存放所有要显示的数据，通过会在控制器的头文件中声明一个 NSArry 或 NSDictionary 来保存数据，然后在其实现文件的 -(void)viewDidLoad 方法中进行初始化。</p>\r\n<pre class=\"prettyprint\">#import &lt;UIKit/UIKit.h&gt;\r\n\r\n@interface Simple_TableViewController : UIViewController\r\n    &lt;UITableViewDelegate, UITableViewDataSource&gt;\r\n{\r\n    // 存放表视图所需要的数据\r\n    NSArray *listData;\r\n}\r\n@property(nonatomic, retain)NSArray *listData;\r\n@end\r\n\r\n#import \"Simple_TableViewController.h\"\r\n\r\n@implementation Simple_TableViewController\r\n@synthesize listData;\r\n\r\n// 初始化数据\r\n- (void)viewDidLoad {\r\n    NSArray *array = [[NSArray alloc]initWithObjects:@\"aaa\", @\"bbb\", \r\n                      @\"ddd\", @\"eee\", @\"fff\", @\"ggg\", @\"hhh\", @\"iii\", \r\n                      @\"jjj\", @\"kkk\", @\"lll\", @\"mm\", @\"nnn\", @\"ooo\", \r\n                      @\"ppp\", @\"qqq\", @\"rrr\", @\"sss\", @\"ttt\", nil];\r\n    self.listData = array;\r\n    [array release];\r\n    [super viewDidLoad];\r\n}</pre>\r\n<p>下面是 UITableViewDataSource&nbsp;的实现，主要是两个函数，一个指定表中有多少行，另一个则设置如何显示每一行，最重要的就是第二个函数设置如何显示每一行。</p>\r\n<pre class=\"prettyprint\">#pragma mark -\r\n#pragma mark Table View Data Source Methods\r\n\r\n// 设置表中的每个分区有多少行，此时默认只有一个分区，也就是指定该表有多少行\r\n-(NSInteger)tableView:(UITableView *)tableView\r\nnumberOfRowsInSection:(NSInteger)section\r\n{\r\n    return [self.listData count];\r\n}\r\n\r\n// 该函数设置具体每一行的显示，并返回设置后的 UITableViewCell 对象\r\n-(UITableViewCell *)tableView:(UITableView *)tableView\r\n        cellForRowAtIndexPath:(NSIndexPath *)indexPath\r\n{\r\n    static NSString *SimpleTableIdentifier = @\"SimpleTableIdentifier\";\r\n    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SimpleTableIdentifier];\r\n    if (cell == nil) {\r\n        cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle\r\n                                      reuseIdentifier:SimpleTableIdentifier]autorelease];\r\n    }\r\n\r\n    // 此处每一行最左边显示的小图标\r\n    UIImage *image = [UIImage imageNamed:@\"bubble.png\"];\r\n    // 此对象为在选中某一行（此时该行应高亮显示）时，应该显示的图标\r\n    UIImage *highlightedimage = [UIImage imageNamed:@\"bubbleSelf.png\"];\r\n    cell.imageView.image = image;\r\n    cell.imageView.highlightedImage = highlightedimage;\r\n\r\n    // 设置每行显示的文本，直接根据当前的行号从数组中去取得\r\n    NSUInteger row = [indexPath row];\r\n    cell.textLabel.text = [listData objectAtIndex:row];\r\n\r\n    // 显示每行文本下面的一些描述信息\r\n    // 注意：需要在创建视图单元时设置 UITableViewCellStyleSubtitle 属性\r\n    // 其它可选参数为：\r\n    // 1. UITableViewCellStyleDefault:只显示一行文本\r\n    // 2. UITableViewCellStyleValue1：详细文本标签会显示在正文的右端\r\n    // 3. UITableViewCellStyleValue2：详细文本标签会显示在正文的左边\r\n    cell.detailTextLabel.text = @\"Mr. Disney\";\r\n\r\n    return cell;\r\n}</pre>\r\n<p>上面的 -(UITableViewCell *)tableView:cellForRowAtIndexPath: 函数中声明了一个静态的字符串实例：</p>\r\n<pre class=\"prettyprint\">static NSString *SimpleTableIdentifier = @\"SimpleTableIdentifier\";</pre>\r\n<p>此字符串充当表示某种单元的键，在此表中我们将只使用一种单元，因此定义一种标识符就可以了。因为表视图只表示当前显示的行，而对于大型表来说，大部分的数据都是无法显示的（需要滚动屏幕），如果视图为表中的每一行都分配一个表视图单元，不管该行当前是否被显示，这都将带来大量开销。</p>\r\n<p>因滚动操作离开屏幕的一些表视图单元，将被放置在一个可以被重用的单元序列中，如果系统运行比较慢，表视图就从序列中删除这些单元，以释放存储空间，不过只要有可用的存储空间，表视图就会重新获取这些单元，以便以后再次使用它们。</p>\r\n<p>当一个表视图单元滚出屏幕时，另一个表视图单元就会从另一边滚动到屏幕上，如果滚动到屏幕上的新行重新使用从屏幕上滚动下来的其中一个单元，系统就会避免不断的创建和释放那些视图时相关的开销。要充分利用些机制，我们需要让表视图给定一个出列单元，该出列单元正是我们需要的类型。通过使用刚刚定义的标识符 SimpleTableIdentifier 即可获取可重用单元：</p>\r\n<pre class=\"prettyprint\">UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:SimpleTableIdentifier];</pre>\r\n<p>如果没有获取到，就重新创建一个。</p>\r\n<p>下面是 UITableViewDelegate&nbsp;的实现，可以设置行的一些行为，如行的单击事件等。</p>\r\n<pre class=\"prettyprint\">#pragma mark -\r\n#pragma mark Table Delegate Methods\r\n\r\n// 该函数设置缩进级别，缩进级别是一个整数：第0行为0，第1行为1，依次类推\r\n-(NSInteger)tableView:(UITableView *)tableView\r\nindentationForRowAtIndexPath:(NSIndexPath *)indexPath\r\n{\r\n    NSUInteger row = [indexPath row];\r\n    return row;\r\n}\r\n\r\n// 该方法会在选择行之前被触发，如果返回 nil 则表示该行不能被选取\r\n// 否则会选中返回值指定的行\r\n-(NSIndexPath *)tableView:(UITableView *)tableView \r\n willSelectRowAtIndexPath:(NSIndexPath *)indexPath\r\n{\r\n    NSUInteger row = [indexPath row];\r\n    if (row == 0) {\r\n        return nil;\r\n    }\r\n    NSArray\r\n    return indexPath;\r\n} \r\n\r\n// 该方法实际上处理选择行后所进行的操作。该项处为弹出一个警告框。\r\n-(void)tableView:(UITableView *)tableView \r\ndidSelectRowAtIndexPath:(NSIndexPath *)indexPath\r\n{\r\n    // 获取行号\r\n    NSUInteger row = [indexPath row];\r\n    // 获取该行显示的文本\r\n    NSString *rowValue = [listData objectAtIndex:row];\r\n\r\n    // 初始化要在警告框中显示的信息\r\n    NSString *message = [[NSString alloc] \r\n                         initWithFormat:@\"You selected %@\", rowValue];\r\n    // 初始化警告框\r\n    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@\"Row Selected!\"\r\n                                                    message:message\r\n                                                   delegate:nil\r\n                                          cancelButtonTitle:@\"Yes, I did\"\r\n                                          otherButtonTitles:nil];\r\n    [alert show];\r\n    [message release];\r\n    [alert release];\r\n    // 取消行的选中状态，弹出对话框后取消该行的高亮显示\r\n    [tableView deselectRowAtIndexPath:indexPath animated:YES];\r\n}</pre>\r\n<p>以上就是设置一个简单的表视图所需要的操作，接下来会介绍如果设置稍微复杂一点的表视图。</p>\r\n<p><strong><span style=\"color: #0000ff;\">二. 定制表视图单元</span></strong></p>\r\n<p>上面只是在一个表视图单元中简单的显示了一行文本，如果要制作复杂的表视图单元该怎么做呢。可以通过两种办法完成，一种是在&nbsp;-(UITableViewCell *)tableView:cellForRowAtIndexPath: 方法中逐个添加子视图；另一种就是使用一个 nib 文件来展示界面，再在代码中加载这个 nib 文件。此处演示在表视图中第一个视图单元显示两行文本，每行文本由两个标签组成：一个显示name，一个显示value，总共是4个子视图。</p>\r\n<p>1. 通过代码添加子视图</p>\r\n<p>在头文件中定义了两个常量，用来标记添加到表视图单元中的两个子视图，每个表视图单元中共有4个子视图，其中2个需要在每一行进行更改，既然要更改，就需要在代码中通过这两个标记来获取这两个子视图。</p>\r\n<pre class=\"prettyprint\">#import &lt;UIKit/UIKit.h&gt;\r\n#define kNameValueTag 1\r\n#define kColorValueTag 2\r\n#define kTableViewRowHeight 66\r\n\r\n@interface CellsViewController : UIViewController \r\n    &lt;UITableViewDelegate, UITableViewDataSource&gt;\r\n{\r\n    // 存放要在表视图中展示的数据\r\n    NSArray *computers;\r\n}\r\n@property(nonatomic, retain)NSArray *computers;\r\n\r\n@end\r\n\r\n#import \"CellsViewController.h\"\r\n\r\n@implementation CellsViewController\r\n@synthesize computers;\r\n\r\n// 初始化\r\n- (void)viewDidLoad {\r\n    NSDictionary *row1 = [[NSDictionary alloc]initWithObjectsAndKeys:\r\n                          @\"MacBook\", @\"Name\", @\"White\", @\"Color\", nil];\r\n    NSDictionary *row2 = [[NSDictionary alloc]initWithObjectsAndKeys:\r\n                          @\"MacBook Pro\", @\"Name\", @\"Silver\", @\"Color\", nil];\r\n    NSDictionary *row3 = [[NSDictionary alloc]initWithObjectsAndKeys:\r\n                          @\"iMac\", @\"Name\", @\"White\", @\"Color\", nil];\r\n    NSDictionary *row4 = [[NSDictionary alloc]initWithObjectsAndKeys:\r\n                          @\"Mac mini\", @\"Name\", @\"White\", @\"Color\", nil];\r\n    NSDictionary *row5 = [[NSDictionary alloc]initWithObjectsAndKeys:\r\n                          @\"Mac Pro\", @\"Name\", @\"Silver\", @\"Color\", nil];\r\n\r\n    // 数组中存放5条记录，表示表视图中共有5行，每行是一个字典类型的数据，\r\n    // 该字典中的数据会显示在每个视图单元中\r\n    NSArray *array = [[NSArray alloc]initWithObjects:\r\n                      row1, row2, row3, row4, row5, nil];\r\n    self.computers = array;\r\n	\r\n    [row1 release];\r\n    [row2 release];\r\n    [row3 release];\r\n    [row4 release];\r\n    [row5 release];\r\n    [array release];\r\n}</pre>\r\n<p>下面是 UITableViewDataSource&nbsp;的实现</p>\r\n<pre class=\"prettyprint\">#pragma mark -\r\n#pragma mark Table Data Source Methods\r\n\r\n// 设置表中共有多少行\r\n-(NSInteger)tableView:(UITableView *)tableView \r\nnumberOfRowsInSection:(NSInteger)section\r\n{\r\n    return [self.computers count];\r\n}\r\n\r\n// 展示每个表视图单元\r\n-(UITableViewCell *)tableView:(UITableView *)tableView\r\n        cellForRowAtIndexPath:(NSIndexPath *)indexPath\r\n{\r\n    static NSString *CellTableIdentifier = @\"CellTableIdentifier\";\r\n    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier\r\n                             :CellTableIdentifier];\r\n    if (cell == nil) {\r\n        cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault\r\n                                      reuseIdentifier:CellTableIdentifier] autorelease];\r\n\r\n        // 添加第一行第一列\r\n        CGRect nameLabelRect = CGRectMake(0, 5, 70, 15);\r\n        UILabel *nameLabel = [[UILabel alloc]initWithFrame:nameLabelRect];\r\n        nameLabel.textAlignment = UITextAlignmentRight;     // 右对齐\r\n        nameLabel.text = @\"Name: \";                         // 显示的文本\r\n        nameLabel.font = [UIFont boldSystemFontOfSize:12];  // 字体大小\r\n        [cell.contentView addSubview:nameLabel];            // 添加到视图单元中\r\n        [nameLabel release];\r\n\r\n        // 添加第二行第一列\r\n        CGRect colorLabelRect = CGRectMake(0, 26, 70, 15);\r\n        UILabel *colorLabel = [[UILabel alloc]initWithFrame:colorLabelRect];\r\n        colorLabel.textAlignment = UITextAlignmentRight;\r\n        colorLabel.text = @\"Color: \";\r\n        colorLabel.font = [UIFont boldSystemFontOfSize:12];\r\n        [cell.contentView addSubview:colorLabel];\r\n        [colorLabel release];\r\n\r\n        // 添加第一行第二列\r\n        CGRect nameValueRect = CGRectMake(80, 5, 200, 15);\r\n        UILabel *nameValue = [[UILabel alloc]initWithFrame:nameValueRect];\r\n        nameValue.tag = kNameValueTag;                      // 设置标记\r\n        [cell.contentView addSubview:nameValue];\r\n        [nameValue release];\r\n\r\n        // 添加第二行第二列\r\n        CGRect colorValueRect = CGRectMake(80, 25, 200, 15);\r\n        UILabel *colorValue = [[UILabel alloc]initWithFrame:colorValueRect];\r\n        colorValue.tag = kColorValueTag;                    // 设置标记\r\n        [cell.contentView addSubview:colorValue];\r\n        [colorValue release];\r\n    }\r\n\r\n    // 获取行号\r\n    NSUInteger row = [indexPath row];\r\n    // 获取要在视图单元中显示的数据 dictionary\r\n    NSDictionary *rowData = [self.computers objectAtIndex:row];\r\n    // 根据已经设置好的标志，获取子视图\r\n    UILabel *name = (UILabel *)[cell.contentView viewWithTag:kNameValueTag];\r\n    name.text = [rowData objectForKey:@\"Name\"];\r\n	\r\n    UILabel *color = (UILabel *)[cell.contentView viewWithTag:kColorValueTag];\r\n    color.text = [rowData objectForKey:@\"Color\"];\r\n	\r\n    return cell;\r\n}\r\n\r\n// 设置行高\r\n-(CGFloat)tableView:(UITableView *)tableView\r\nheightForRowAtIndexPath:(NSIndexPath *)indexPath\r\n{\r\n    return kTableViewRowHeight;\r\n}</pre>\r\n<p>2. 使用 UITableViewCell 的自定义子类</p>\r\n<p>向 Classes 文件夹中添加一个 UITableViewCell 的子类名为：CustomCell。只要向新类中添加两个输出口，分别指向要显示值的两个子视图即可。</p>\r\n<pre class=\"prettyprint\">#import &lt;UIKit/UIKit.h&gt;\r\n\r\n@interface CustomCell : UITableViewCell {\r\n    UILabel *nameLabel;\r\n    UILabel *colorLabel;\r\n}\r\n@property(nonatomic, retain)IBOutlet UILabel *nameLabel;\r\n@property(nonatomic, retain)IBOutlet UILabel *colorLabel;\r\n\r\n@end\r\n\r\n@implementation CustomCell\r\n@synthesize nameLabel;\r\n@synthesize colorLabel;\r\n\r\n// 其余部分默认，不需要改变</pre>\r\n<p>右击 Resouces 文件夹，依次选择 Add-&gt;New File...-&gt;User Interface-&gt;Empty XIB，输入名称：CustomCell.xib</p>\r\n<p>1) 打开 CustomCell.xib ，主窗口中只有两个图标，向主窗口中添加一个 Table View Cell。<br />2) 选中并打开标识检查器，将类从 UITableViewCell 改为 CustomCell。<br />3) 打开大小检查器，把高度从44改为65。<br />4) 打开属性检查器，其中一个字段是 Identifier，它是我们在代码中使用过的可重用标识符，即 -(UITableViewCell *) tableView:cellForRowAtIndexPath: 方法中定义的静态变量。把 Identifier 设置为 CustomCellIdentifier&nbsp;<br />5) 在主窗口中双击 Custom Cell 图标打开一个新的窗口，从库中拖放一个 View 到 &nbsp;Custom Cell 窗口中。<br />6) 选中 View 打开大小检查器进行如下设置 x:0 &nbsp;y:0 &nbsp;w:320 &nbsp;h:65，使 View 的大小符合 Custom Cell 窗口的大小。<br />7) 最后拖4个标签到 Custom Cell 窗口中并设置好输出口，设置输出口时按下Control键将 Custom Cell 图标(不是File\'s Owner)拖到新标签上。<br />保存 nib 并将其关闭，返回 Xcode 窗口。&nbsp;</p>\r\n<p>代码部分与上一个示例的区别就在 &nbsp;-(UITableViewCell *) tableView:cellForRowAtIndexPath: 函数，其它的基本一样</p>\r\n<pre class=\"prettyprint\">-(UITableViewCell *)tableView:(UITableView *)tableView\r\n        cellForRowAtIndexPath:(NSIndexPath *)indexPath\r\n{\r\n    static NSString *CellTableIdentifier = @\"CellTableIdentifier\";\r\n    CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:\r\n                                      CellTableIdentifier];\r\n    if (cell == nil) {\r\n        NSArray *nib = [[NSBundle mainBundle]loadNibNamed:@\"CustomCell\"\r\n                                                    owner:self options:nil];\r\n        for (id oneObject in nib) {\r\n            if ([oneObject isKindOfClass:[CustomCell class]]) {\r\n                cell = (CustomCell *)oneObject;\r\n            }\r\n        }\r\n    }\r\n    NSUInteger row = [indexPath	row];\r\n    NSDictionary *rowData = [self.computers objectAtIndex:row];\r\n    cell.colorLabel.text = [rowData objectForKey:@\"Name\"];\r\n    cell.nameLabel.text = [rowData objectForKey:@\"Name\"];\r\n    return cell;\r\n}</pre>\r\n<p>由于我们已在 nib 文件中设计了一个表视图，所以如果没有可重用的单元，我们会从 nib 文件中加载一个。在加载 nib 文件时，得到的是一个数组，其中包含了 nib 文件中的所有对象。我们只要遍历一次数组，依次判断其中的对象是否为 CustomCell 类的一个实例。如果是则直接赋给 cell 变量即可</p>\r\n<p>&nbsp;</p>','',1,1,'2011-08-17 11:42:44','2011-08-17 11:42:44',17,'iOS, iPhone',5),(31,'Objective-C解析XML','objective-c-xml',1,'<p>一. 使用 NSXMLParser 类</p>\r\n<p>NSXMLParser 使用的是事件驱动的方式来解析 XML 文件，读到了某个标签或内容都会触发相应的方法。要实现对 XML 的解析可以自定义一个解析类，该必须实现 NSXMLParserDelegate 委托，该委托才是真正触发事件的地方。它有如下几个重要的方法：</p>\r\n<pre class=\"prettyprint\">// 该方法在读取一个新的节点时触发\r\n- (void) parser:(NSXMLParser *)parser\r\ndidStartElement:(NSString *)elementName \r\n   namespaceURI:(NSString *)namespaceURI \r\n  qualifiedName:(NSString *)qName \r\n     attributes:(NSDictionary *)attributeDict\r\n\r\n// 该方法在读取节点中的内容时触发\r\n- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string\r\n\r\n// 该方法在读完一个节点后触发\r\n- (void) parser:(NSXMLParser *)parser \r\n  didEndElement:(NSString *)elementName \r\n   namespaceURI:(NSString *)namespaceURI \r\n  qualifiedName:(NSString *)qName\r\n\r\n// 该方法在读完整个 XML 后触发\r\n- (void)parserDidEndDocument:(NSXMLParser *)parser</pre>\r\n<p>以下是实例代码：</p>\r\n<pre class=\"prettyprint\">#import &lt;Foundation/Foundation.h&gt;\r\n\r\n@interface MyXMLParser : NSObject\r\n    &lt;NSXMLParserDelegate&gt;\r\n{\r\n    NSXMLParser *xml;\r\n}\r\n\r\n@property(assign) NSXMLParser *xml;\r\n-(MyXMLParser *)initWithFile: (NSString *)path;\r\n\r\n@end\r\n\r\n@implementation MyXMLParser\r\n@synthesize xml;\r\n\r\n// 初始化函数\r\n-(MyXMLParser *)initWithFile:(NSString *)path\r\n{\r\n    // 根据指定的路径，读取磁盘上的文件\r\n    NSFileManager *fm = [NSFileManager defaultManager];\r\n    if ([fm fileExistsAtPath:path] == YES) {\r\n        NSData *data = [fm contentsAtPath:path];\r\n        xml = [[NSXMLParser alloc] initWithData: data];\r\n        [xml setShouldProcessNamespaces:NO];\r\n        [xml setShouldReportNamespacePrefixes:NO];\r\n        [xml setShouldResolveExternalEntities:NO];\r\n        [data release];\r\n        \r\n        // 把委托设为自己\r\n        [xml setDelegate:self];\r\n    }\r\n    else {\r\n        NSLog(@\"%@ not exists\", path);\r\n    }\r\n\r\n    return self;\r\n}\r\n\r\n//解析器，从两个结点之间读取内容\r\n-(void) parser:(NSXMLParser *)parser foundCharacters:(NSString *)string\r\n{\r\n    NSLog(@\"%@\",string);\r\n}\r\n\r\n//获得结点结尾的值\r\n-(void) parser:(NSXMLParser *)parser \r\n didEndElement:(NSString *)elementName \r\n  namespaceURI:(NSString *)namespaceURI \r\n qualifiedName:(NSString *)qName\r\n{\r\n    //NSLog(@\"Name:%@\",elementName);\r\n}\r\n\r\n//获得结点头的值\r\n-(void) parser:(NSXMLParser *)parser\r\ndidStartElement:(NSString *)elementName \r\n  namespaceURI:(NSString *)namespaceURI \r\n qualifiedName:(NSString *)qName \r\n    attributes:(NSDictionary *)attributeDict\r\n{\r\n    NSLog(@\"Name:%@\",elementName);\r\n}\r\n\r\n-(void)dealloc\r\n{\r\n    [xml release];\r\n    [super dealloc];\r\n}\r\n\r\n@end\r\n\r\nint main (int argc, const char * argv[]) {\r\n    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];\r\n\r\n    MyXMLParser *parser = [[MyXMLParser alloc] initWithFile:@\"test.xml\"];\r\n    // 开始解析\r\n    BOOL success = [parser.xml parse];\r\n    if (success) {\r\n        NSLog(@\"Read XML Success\");\r\n    }\r\n    else {\r\n        NSLog(@\"Read XML Failure\");\r\n    }\r\n\r\n    [pool drain];\r\n    return 0;\r\n}\r\n</pre>','',1,1,'2011-08-18 10:42:13','2011-08-18 10:42:13',17,'objective-c, xml, iPhone',2),(32,'设置Cookie','javascript-cookie',1,'<p>Javascript 中对 cookie 操作如下：</p>\r\n<pre class=\"prettyprint\">// 设置 cookie\r\nfunction SetCookie(name, value)\r\n{\r\n    document.cookie = name+\"=\"+escape(value);\r\n}\r\n// 获取 cookie\r\nfunction GetCookie(name)\r\n{\r\n    var ret = \"\";\r\n    var strCookie=document.cookie;\r\n    var arrCookie=strCookie.split(\"; \");\r\n    for(var i=0;i&lt;arrCookie.length;i++)\r\n    {\r\n        var arr=arrCookie[i].split(\"=\");\r\n        if(name==arr[0])\r\n        {\r\n            ret=unescape(arr[1]);\r\n            break;\r\n        }\r\n    }\r\n    return ret;\r\n}\r\n// 删除 cookie\r\nfunction DelCookie(name)\r\n{   \r\n    var exp = new Date();   \r\n    exp.setTime(exp.getTime() - 1);   \r\n    var cval=GetCookie(name);   \r\n    if(cval!=null &amp;&amp; cval!=\"\") \r\n        document.cookie= name + \"=\"+cval+\"; expires=\"+exp.toGMTString();   \r\n}</pre>\r\n<p>&nbsp;</p>','',1,1,'2011-08-18 16:19:58','2011-08-18 16:19:58',8,'javascript, cookie',3),(33,'Ext.data.Record','ext-data-record',1,'<p>Ext.data.Record就是一个设定了内部数据类型的对象，它是Ext.data.Store的最基本组成部分。如果把Ext.data.Store看作是一张二维表，那么它的每一行就对应一个Ext.data. Record实例。</p>\r\n<p>Ext.data.Record的主要功能是保存数据，并且在内部数据发生改变时记录修改的状态，它还可以保留修改之前的原始值。我们使用Ext.data.Record时通常都是由create()函数开始，首先用create()函数创建一个自定义的Record类型，如下面的代码所示。</p>\r\n<pre class=\"prettyprint\">var PersonRecord = Ext.data.Record.create([\r\n    {name: \'name\', type: \'string\'},\r\n    {name: \'sex\', type: \'int\'}\r\n]);</pre>\r\n<p>PersonRecord就是我们定义的新类型，包含字符串类型的name和整数类型的sex两个属性，然后我们使用new关键字创建PersonRecord的实例，如下面的代码所示。</p>\r\n<pre class=\"prettyprint\">var boy = new PersonRecord({\r\n    name: \'boy\',\r\n    sex: 0\r\n});</pre>\r\n<p>创建对象时，可以直接通过构造方法为对象赋予初始值，将\'boy\'赋值给name，0赋值给sex。现在，我们得到了PersonRecord的实例boy，如何才能得到它的属性呢？以下三种方式都可以获得boy中name属性的数据，如下面的代码所示。</p>\r\n<pre class=\"prettyprint\">alert(boy.data.name);\r\nalert(boy.data[\'name\']);\r\nalert(boy.get(\'name\'));</pre>\r\n<p>这里涉及Ext.data.Record的data属性，这是定义在Ext.data.Record中的一个公共属性，用于保存当前record对象的所有数据。它是一个JSON对象，可以直接从它里面获得需要的数据。可以通过Ext.data.Record的get()函数方便地从data属性中获得指定的属性值。如果我们需要修改boy中的数据，请不要使用以下方式直接操作data，如下面的代码所示。</p>\r\n<pre class=\"prettyprint\">boy.data.name = \'boy name\';\r\nboy.data[\'name\'] = \'boy name\';</pre>\r\n<p>而应该使用set()函数，如下面的代码所示。</p>\r\n<pre class=\"prettyprint\">boy.set(\'name\', \'body name\');</pre>\r\n<p>set()函数会判断属性值是否发生了改变，如果改变了，就要将当前对象的dirty属性设置为true，并将修改之前的原始值放入modified对象中，供其他函数使用。如果直接操作data中的值，record就无法记录属性数据的修改情况。</p>\r\n<p>Record的属性数据被修改后，我们可以执行如下几种操作。</p>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td>commit()（提交）</td>\r\n<td>这个函数的效果是设置dirty为false，并删除modified中保存的原始数据。</td>\r\n</tr>\r\n<tr>\r\n<td>reject()（撤销）</td>\r\n<td>这个函数的效果是将data中已经修改了的属性值都恢复成modified中保存的原始数据，然后设置dirty为false，并删除保存原始数据的modified对象。</td>\r\n</tr>\r\n<tr>\r\n<td>getChanges()获得修改的部分</td>\r\n<td>这个函数会把data中经过修改的属性和数据放在一个JSON对象里并返回。例如上例中，getChanges()返回的结果是{name:&rsquo;body name&rsquo;}。</td>\r\n</tr>\r\n<tr>\r\n<td>isModified()</td>\r\n<td>判断当前record中的数据是否被修改。</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>Ext.data.Record还提供了用于复制record实例的函数copy()。</p>\r\n<pre class=\"prettyprint\">var copyBoy = boy.copy();</pre>\r\n<p>这样我们就得到了boy的一个副本，它里面包含了boy的data数据，但copy()函数不会复制dirty和modified等额外的属性值。</p>','',1,1,'2011-08-29 16:54:11','2011-08-29 16:54:11',18,'ExtJS',3),(34,'Ext.data.Store','ext-data-store',1,'<p>Ext.data.Store是EXT中用来进行数据交换和数据交互的标准中间件，无论是Grid还是ComboBox，都是通过它实现数据读取、类型转换、排序分页和搜索等操作的。</p>\r\n<p>Ext.data.Store中有一个Ext.data.Record数组，所有数据都存放在这些Ext.data. Record实例中，为后面的读取和修改操作做准备。</p>\r\n<p><strong>一、Ext.data.Store基本应用</strong></p>\r\n<p>在使用之前，首先要创建一个Ext.data.Store的实例，如下面的代码所示。</p>\r\n<pre class=\"prettyprint\">var data = [\r\n    [\'boy\', 0],\r\n    [\'girl\', 1]\r\n];\r\n \r\nvar store = new Ext.data.Store({\r\n    proxy: new Ext.data.MemoryProxy(data),\r\n    reader: new Ext.data.ArrayReader({}, PersonRecord)\r\n});\r\n\r\nstore.load();</pre>\r\n<p>每个store最少需要两个组件的支持，分别是proxy和reader，proxy用于从某个途径读取原始数据，reader用于将原始数据转换成Record实例。</p>\r\n<p>这里我们使用的是Ext.data.MemoryProxy和Ext.data.ArrayReader，将data数组中的数据转换成对应的几个PersonRecord实例，然后放入store中。store创建完毕之后，执行store.load()实现这个转换过程。经过转换之后，store里的数据就可以提供给Grid或ComboBox使用了，这就是Ext.data. Store的最基本用法。</p>\r\n<p><strong>二、Ext.data.Store对数据进行排序</strong></p>\r\n<p>Ext.data.Store提供了一系列属性和函数，利用它们对数据进行排序操作。可以在创建Ext.data.Store时使用sortInfo参数指定排序的字段和排序方式，如下面的代码所示。</p>\r\n<pre class=\"prettyprint\">var store = new Ext.data.Store({\r\n    proxy: new Ext.data.MemoryProxy(data),\r\n    reader: new Ext.data.ArrayReader({}, PersonRecord),\r\n    sortInfo: {field: \'name\', direction: \'DESC\'}\r\n});</pre>\r\n<p>这样，在store加载数据之后，就会自动根据name字段进行降序排列。对store使用store.setDefaultSort(\'name\',\'DESC\');也会达到同样效果。</p>\r\n<p>也可以在任何时候调用sort()函数，比如store.sort(\'name\', \'DESC\');，对store中的数据进行排序。如果我们希望获得store的排序信息，可以调用getSortState()函数，返回的是类似{field: \"name\", direction: \" DESC\"}的JSON对象。</p>\r\n<p>与排序相关的参数还有remoteSort，这个参数是用来实现后台排序功能的。当设置为remoteSort:true时，store会在向后台请求数据时自动加入sort和dir两个参数，分别对应排序的字段和排序的方式，由后台获取并处理这两个参数，在后台对所需数据进行排序操作。remoteSort:true也会导致每次执行sort()时都要去后台重新加载数据，而不能只对本地数据进行排序。</p>\r\n<p>三、Ext.data.Store加载及显示数据</p>\r\n<p>store创建好后，需要调用load()函数加载数据，加载成功后才能对store中的数据进行操作。load()调用的完整过程如下面的代码所示。</p>\r\n<pre class=\"prettyprint\">store.load({\r\n    params: {start:0,limit:20},\r\n    callback: function(records, options, success){\r\n        Ext.Msg.alert(\'info\', \'加载完毕\');\r\n    },\r\n    scope: store,\r\n    add: true\r\n});</pre>\r\n<p>params是在store加载时发送的附加参数。&nbsp; <br />callback是加载完毕时执行的回调函数，它包含3个参数：records参数表示获得的数据，options表示执行load()时传递的参数，success表示是否加载成功。<br />Scope用来指定回调函数执行时的作用域。&nbsp; <br />Add为true时，load()得到的数据会添加在原来的store数据的末尾，否则会先清除之前的数据，再将得到的数据添加到store中。</p>\r\n<p>一般来说，为了对store中的数据进行初始化，load()函数只需要执行一次。如果用params参数指定了需要使用的参数，以后再次执行reload()重新加载数据时，store会自动使用上次load()中包含的params参数内容。</p>\r\n<p>如果有一些需要固定传递的参数，也可以使用baseParams参数执行，它是一个JSON对象，里面的数据会作为参数发送给后台处理，如下面的代码所示。</p>\r\n<pre class=\"prettyprint\">store.baseParams.start = 0;\r\nstore.baseParams.limit = 20;</pre>\r\n<p>为store加载数据之后，有时不需要把所有数据都显示出来，这时可以使用函数filter和filterBy对store中的数据进行过滤，只显示符合条件的部分，如下面的代码所示。</p>\r\n<pre class=\"prettyprint\">filter( String field, String/RegExp value, [Boolean anyMatch], [Boolean caseSensitive] ) : void</pre>\r\n<p>filter()函数的用法与之前谈到的find()相似，如下面的代码所示。</p>\r\n<pre class=\"prettyprint\">store.filter(\'name\', \'boy\');</pre>\r\n<p>对应的filterBy()与findBy()类似，也可以在自定义的函数中实现各种复杂判断，如下面的代码所示。</p>\r\n<pre class=\"prettyprint\">store.filterBy(function(record) {\r\n    return record.get(\'name\') == \'girl\' &amp;&amp; record.get(\'sex\') == 1;\r\n});</pre>\r\n<p>如果想取消过滤并显示所有数据，那么可以调用clearFilter()函数，如下面的代码所示。</p>\r\n<pre class=\"prettyprint\">store.clearFilter();</pre>\r\n<p>如果想知道store上是否设置了过滤器，可以通过isFiltered()函数进行判断。</p>\r\n<p><strong>四、Ext.data.Store其他功能</strong></p>\r\n<p>除了上面提到的数据获取、排序、更新、显示等功能外，store还提供了其他一些功能函数。</p>\r\n<p>collect( String dataIndex, [Boolean allowNull], [Boolean bypassFilter] ) : Array</p>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 1462px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">collect函数获得指定的dataIndex对应的那一列的数据，当allowNull参数为true时，返回的结果中可能会包含null、undefined或空字符串，否则collect函数会自动将这些空数据过滤掉。当bypassFilter参数为true时，collect的结果不会受查询条件的影响，无论查询条件是什么都会忽略掉，返回的信息是所有的数据，如下面的代码所示。</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 1462px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">alert(store.collect(\'name\'));</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 1462px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">这样会获得所有name列的值，示例中返回的是包含了\'boy\'和\'girl\'的数组。collect函数获得指定的dataIndex对应的那一列的数据，当allowNull参数为true时，返回的结果中可能会包含null、undefined或空字符串，否则collect函数会自动将这些空数据过滤掉。当bypassFilter参数为true时，collect的结果不会受查询条件的影响，无论查询条件是什么都会忽略掉，返回的信息是所有的数据，如下面的代码所示。alert(store.collect(\'name\'));这样会获得所有name列的值，示例中返回的是包含了\'boy\'和\'girl\'的数组。</div>\r\n<p style=\"margin-left: 20px;\">collect函数获得指定的dataIndex对应的那一列的数据，当allowNull参数为true时，返回的结果中可能会包含null、undefined或空字符串，否则collect函数会自动将这些空数据过滤掉。当bypassFilter参数为true时，collect的结果不会受查询条件的影响，无论查询条件是什么都会忽略掉，返回的信息是所有的数据，如下面的代码所示。 <br />alert(store.collect(\'name\')); <br />这样会获得所有name列的值，示例中返回的是包含了\'boy\'和\'girl\'的数组。</p>\r\n<p>getTotalCount()</p>\r\n<p style=\"margin-left: 20px;\">用于在翻页时获得后台传递过来的数据总数。如果没有设置翻页，get- TotalCount()的结果与getCount()相同，都是返回当前的数据总数，如下面的代码所示。<br />alert(store.getTotalCount());</p>\r\n<p>indexOf(Ext.data.Record record)和indexOfId(String id)</p>\r\n<p style=\"margin-left: 20px;\">根据record或record的id获得record对应的行号，如下面的代码所示。<br />alert(store.indexOf(store.getAt(1)));<br />alert(store.indexOfId(1001));</p>\r\n<p>loadData(object data, [Boolean append])</p>\r\n<p style=\"margin-left: 20px;\">从本地JavaScript变量中读取数据，append为true时，将读取的数据附加到原数据后，否则执行整体更新，如下面的代码所示。<br />store.loadData(data, true);</p>\r\n<p>Sum(String property, Number start, Number end):Number</p>\r\n<p style=\"margin-left: 20px;\">用于计算某一个列从start到end的总和，如下面的代码所示。<br />alert(store.sum(\'sex\'));</p>','',1,1,'2011-08-29 13:10:24','2011-08-29 13:10:24',18,'ExtJS',NULL),(35,'Ext.grid.ColumnModel','ext-grid-columnmodel',1,'<p>该类用于定义表格的列模型，其配置项是一个由&ldquo;列定义&rdquo;组成的数组，还可以是JSON组成的对象。其中也定义很多操作列的属性、方法 比如：查找某个列，隐藏某个列等</p>\r\n<p>一、用数组的方式配置ColumnModel</p>\r\n<pre class=\"prettyprint\">var colModel = new Ext.grid.ColumnModel([\r\n    { header:\'编号\', dataIndex:\'id\'，width:200},\r\n    { header:\'名称\', dataIndex:\'name\',width:300}\r\n]};</pre>\r\n<p>注意：上述写法ColumnModel的构造参数必须是一个数组，哪怕只有一列。</p>\r\n<p>二、用JSON对象的方式配置ColumnModel</p>\r\n<pre class=\"prettyprint\">var colModel = new Ext.grid.ColumnModel({\r\n    columns:[    --列集合的定义\r\n        { header:\'编号\', dataIndex:\'id\'，width:200},\r\n        { header:\'名称\', dataIndex:\'name\',width:300}\r\n    ],\r\n    defaults:{   --公共属性配置\r\n        sortable:true,\r\n        menuDisabled:true,\r\n        width:100\r\n    },\r\n    listeners:{    --监听事件\r\n        \"widthchange\":function(cm,colIndx,width){\r\n            saveConfig(colIndex,width);\r\n        }   \r\n    }\r\n});</pre>\r\n<p>这种写法的好处是更加的面向对象，也便于继承。</p>\r\n<p>三、常用属性介绍：</p>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td>header</td>\r\n<td>设置列标题</td>\r\n</tr>\r\n<tr>\r\n<td>dataIndex</td>\r\n<td>设置列对应数据源的name值</td>\r\n</tr>\r\n<tr>\r\n<td>editor</td>\r\n<td>设置在编辑状态下,指定编辑器</td>\r\n</tr>\r\n<tr>\r\n<td>align</td>\r\n<td>设置列标题的对齐方式</td>\r\n</tr>\r\n<tr>\r\n<td>width</td>\r\n<td>设置列的宽度</td>\r\n</tr>\r\n<tr>\r\n<td>css</td>\r\n<td>设置列头的CSS样式</td>\r\n</tr>\r\n<tr>\r\n<td>fixed</td>\r\n<td>表示列头的宽度是否可以改变，默认为true</td>\r\n</tr>\r\n<tr>\r\n<td>hidden</td>\r\n<td>表示是否是隐藏列，默认为false</td>\r\n</tr>\r\n<tr>\r\n<td>hideable</td>\r\n<td>表示是否禁止用户隐藏该列，默认为false</td>\r\n</tr>\r\n<tr>\r\n<td>menuDisabled</td>\r\n<td>表示是否禁止列菜单 默认为false</td>\r\n</tr>\r\n<tr>\r\n<td>resizable</td>\r\n<td>表示是否禁止列可变大小,默认为false</td>\r\n</tr>\r\n<tr>\r\n<td>sortable</td>\r\n<td>表示列是否可排序,默认为false</td>\r\n</tr>\r\n<tr>\r\n<td>tooltip</td>\r\n<td>表示列头部显示的提示文字</td>\r\n</tr>\r\n<tr>\r\n<td>renderer</td>\r\n<td>用于加工单元格的原始数据，需要把一个函数传递给该属性，定义函数如：function  renderName(value, metadata, record, rowIndex, columnIndex, store){} \r\n<table>\r\n<tbody>\r\n<tr>\r\n<td>value</td>\r\n<td>当前单元格的值</td>\r\n</tr>\r\n<tr>\r\n<td>metadata</td>\r\n<td>一些基本信息，可以设置CSS样式和 attribute 属性</td>\r\n</tr>\r\n<tr>\r\n<td>record</td>\r\n<td>表示该行的数据，其实就是一个Ext.data.Record类型的对象，可以通过record.data[\"name\"]访问某列的值</td>\r\n</tr>\r\n<tr>\r\n<td>rowIndex</td>\r\n<td>行号，不是从头往下数的意思，而是计算了分页以后的结果。</td>\r\n</tr>\r\n<tr>\r\n<td>columnIndex</td>\r\n<td>列号</td>\r\n</tr>\r\n<tr>\r\n<td>store</td>\r\n<td>实际上这个是你构造表格时候传递的ds，也就是说表格里所有的数据，其类型为 Ext.data.Store ，你都可以随便调用</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n</td>\r\n</tr>\r\n<tr>\r\n<td>id</td>\r\n<td>列的标识，列的所有单元格包括头部都是用这个值来创建CSS的class属性, 格式为：       \r\n<table>\r\n<tbody>\r\n<tr>\r\n<td>x-grid3-td-id</td>\r\n<td>单元格样式</td>\r\n</tr>\r\n<tr>\r\n<td>x-grid3-td-hd-id</td>\r\n<td>列头样式</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>四、常用方法介绍：</p>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td>findColumnIndex</td>\r\n<td>根据dataIndex里的值，返回列的索引</td>\r\n</tr>\r\n<tr>\r\n<td>getColumnById</td>\r\n<td>返回指定ID的列</td>\r\n</tr>\r\n<tr>\r\n<td>getColumnCount</td>\r\n<td>返回列数</td>\r\n</tr>\r\n<tr>\r\n<td>getColumnId</td>\r\n<td>返回指定index列的ID</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>该类有很多方法来操作列，具体查看<a href=\"http://dev.sencha.com/deploy/ext-3.3.1/docs/\">API</a></p>','',1,1,'2011-08-29 23:01:19','2011-08-29 23:01:19',18,'ExtJS',NULL),(36,'使用Outlook设置Gmail邮箱','outlook-gmail-settings',1,'<p>要在 Outlook&nbsp;中设置 Gmail&nbsp;邮箱，共有两个步骤，第一步在是 Gmail 中设置，第二步再配置 Outlook</p>\r\n<p><strong>第一步：启用 Gmail 中的 POP</strong></p>\r\n<p>1） 进入 Gmail 邮箱，点击右上角的设置，进入 Gmail 设置页面<br />2） 点击项部橙色区域的&ldquo;转发和POP/IMAP&rdquo;标签页<br />3） 配置&ldquo;POP 下载&rdquo;中的选项，选择&ldquo;针对所有邮件启用 POP（包括已经下载的邮件）&rdquo;单选项<br />4） 设置完成后，单击&ldquo;保存更改&rdquo;按钮&nbsp;</p>\r\n<p><strong>第二步：配置 Outlook</strong></p>\r\n<p>1） 在 Outlook 中打开电子邮件帐户的设置对话框<br />2） 在&ldquo;电子邮件地址&rdquo;框中填入完整的 Gmail 电子邮件地址<br />3） 在&ldquo;接收邮件服务器&rdquo;中输入&ldquo;pop.gmail.com&rdquo;，在&ldquo;发送邮件服务器(SMTP)&rdquo;框中输入&ldquo;smtp.gmail.com&rdquo;<br />4） 在&ldquo;帐户名&rdquo;中输入完整的 Gmail 用户名(包括@gmail.com)，再输入密码<br />5） 点击右侧的&ldquo;详细设定&rdquo;按钮 ，进入&ldquo;发送服务器&rdquo;标签，选中\"我的发送服务器 (SMTP) 要求身份验证旁\"边的复选框，然后选择\"使用与接收邮件服务器相同的设置\"。<br />6） 选择&ldquo;高级&rdquo;标签，&ldquo;接收邮件服务器(POP3)&rdquo;设置为 993，且必须使用 SSL 加密。&nbsp;&ldquo;发送服务器(SMTP)&rdquo;设置为 587，且使用 LTS 加密<br />7） 确定，完成&nbsp;</p>','',1,1,'2011-09-01 22:59:58','2011-09-01 22:59:58',1,'',NULL),(37,'SQL Server带参数的视图','sqlserver-view-parameter',1,'<p>在SQL Server中没有带视图的参数，可以通过函数实现</p>\r\n<pre class=\"prettyprint\">CREATE function v_name(@id int)\r\nreturns table\r\nas\r\nreturn (select * FROM dbo.[Names] WHERE id=@id)</pre>\r\n<p>然后可以通过类似查询表或视图一样来查询这个函数，只要在函数中加入参数值就行了，例如</p>\r\n<pre class=\"prettyprint\">SELECT * FROM dbo.v_name(2)</pre>','',1,1,'2011-10-29 23:43:39','2011-10-29 23:43:39',12,'',2),(38,'如何在C#中运用正则表达式','csharp-regular-expression',1,'<p><strong>命名空间简介：</strong></p>\r\n<p><span style=\"white-space: pre;\"> </span>在C#中运用正则表达式就会用到System.Text.RegularExpressions.dll文件，其命名空间就是System.Text.RegularExpressions。其中包含如下一些类和方法。</p>\r\n<table>\r\n<thead> \r\n<tr>\r\n<th style=\"width: 120px;\">类名</th><th>说明</th>\r\n</tr>\r\n</thead> \r\n<tbody>\r\n<tr>\r\n<td>Capture</td>\r\n<td>表示单个子表达式捕获中的结果。Capture 表示单个成功捕获中的一个子字符串。</td>\r\n</tr>\r\n<tr>\r\n<td>CaptureCollection</td>\r\n<td>表示一个捕获子字符串序列。CaptureCollection 返回由单个捕获组执行的捕获的集合。</td>\r\n</tr>\r\n<tr>\r\n<td>Group</td>\r\n<td>Group 表示单个捕获组的结果。由于存在数量词，一个捕获组可以在单个匹配中捕获零个、一个或更多的字符串，因此 Group 提供 Capture 对象的集合。</td>\r\n</tr>\r\n<tr>\r\n<td>GroupCollection</td>\r\n<td>表示捕获组的集合。GroupCollection 返回单个匹配中的捕获组的集合。</td>\r\n</tr>\r\n<tr>\r\n<td>Match</td>\r\n<td>表示单个正则表达式匹配的结果。</td>\r\n</tr>\r\n<tr>\r\n<td>MatchCollection</td>\r\n<td>表示通过以迭代方式将正则表达式模式应用于输入字符串所找到的成功匹配的集合。</td>\r\n</tr>\r\n<tr>\r\n<td>Regex</td>\r\n<td>表示不可变的正则表达式。</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<table>\r\n<thead> \r\n<tr>\r\n<th style=\"width: 120px;\">Regex类常用方法</th><th>说明</th>\r\n</tr>\r\n</thead> \r\n<tbody>\r\n<tr>\r\n<td>Escape</td>\r\n<td>通过将最少量的一组字符（\\、*、+、?、|、{、[、(、)、^、$、.、# 和空白）替换为其转义码，将这些字符转义。 此操作指示正则表达式引擎以字面意义而非按元字符解释这些字符。</td>\r\n</tr>\r\n<tr>\r\n<td>IsMatch</td>\r\n<td>指示 Regex 构造函数中指定的正则表达式在指定的输入字符串中是否找到了匹配项。该方法返回一个布尔值</td>\r\n</tr>\r\n<tr>\r\n<td>Match</td>\r\n<td>在指定的输入字符串中搜索 Regex 构造函数中指定的正则表达式的第一个匹配项。</td>\r\n</tr>\r\n<tr>\r\n<td>Matches</td>\r\n<td>在指定的输入字符串中搜索正则表达式的所有匹配项。</td>\r\n</tr>\r\n<tr>\r\n<td>Replace</td>\r\n<td>在指定的输入字符串内，使用指定的替换字符串替换与某个正则表达式模式匹配的所有字符串。</td>\r\n</tr>\r\n<tr>\r\n<td>Split</td>\r\n<td>在由 Regex 构造函数指定的正则表达式模式所定义的位置，拆分指定的输入字符串。</td>\r\n</tr>\r\n<tr>\r\n<td>Unescape</td>\r\n<td>转换输入字符串中的任何转义字符。</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p><strong>简单匹配：</strong></p>\r\n<p>我们首先从使用Regex、Match类的简单表达式开始学习。</p>\r\n<pre class=\"prettyprint\">Match m = Regex.Match(\"abracadabra\", \"(a|b|r)+\");</pre>\r\n<p>我们现在有了一个可以用于测试的Match类的实例，例如：if (m.Success)...&nbsp;如果想使用匹配的字符串，可以把它转换成一个字符串：</p>\r\n<pre class=\"prettyprint\">Console.WriteLine(\"Match=\"+m.ToString());</pre>\r\n<p>这个例子可以得到如下的输出: Match=abra。这就是匹配的字符串了。</p>\r\n<p><strong>字符串的替换：</strong></p>\r\n<p>简单字符串的替换非常直观。例如下面的语句：</p>\r\n<pre class=\"prettyprint\">string s = Regex.Replace(\"abracadabra\", \"abra\", \"zzzz\");</pre>\r\n<p>它返回字符串zzzzcadzzzz，所有匹配的字符串都被替换成了zzzzz。</p>\r\n<p>现在我们来看一个比较复杂的字符串替换的例子：</p>\r\n<pre class=\"prettyprint\">string s = Regex.Replace(\" abra \", @\"^\\s*(.*?)\\s*$\", \"$1\");</pre>\r\n<p>这个语句返回字符串abra，其前导和后缀的空格都去掉了。</p>\r\n<p>上面的模式对于删除任意字符串中的前导和后续空格都非常有用。在C#中，我们还经常使用字母字符串，在一个字母字符串中，编译程序不把字符&ldquo; \\&rdquo; 作为转义字符处理。在使用字符&ldquo;\\&rdquo;指定转义字符时，@\"...\"是非常有用的。另外值得一提的是$1在字符串替换方面的使用，它用来保存第一个子匹配，可以用$2, $3等继续引用。</p>\r\n<p><strong>匹配引擎的细节：</strong></p>\r\n<p>现在，我们通过一个组结构来理解一个稍微复杂的例子。看下面的例子：</p>\r\n<pre class=\"prettyprint\">string text = \"abracadabra1abracadabra2abracadabra3\";\r\n\r\nstring pat = @\"\r\n            (#第一个组的开始\r\n            abra#匹配字符串abra\r\n            (#第二个组的开始\r\n            cad#匹配字符串cad\r\n            )?#第二个组结束（可选）\r\n            )#第一个组结束\r\n            +#匹配一次或多次\r\n            \";\r\n\r\n//消除模式中的非转义空白并启用由 # 标记的注释。 \r\n//但是，IgnorePatternWhitespace 值不会影响或消除字符类中的空白。\r\nRegex r = new Regex(pat, RegexOptions.IgnorePatternWhitespace);\r\n//获得组号码的清单 \r\nint[] gnums = r.GetGroupNumbers();\r\n//首次匹配 \r\nMatch m = r.Match(text);\r\nConsole.WriteLine(m.Value);\r\nwhile (m.Success)\r\n{\r\n    //从组1开始 \r\n    for (int i = 1; i &lt; gnums.Length; i++)\r\n    {\r\n        Group g = m.Groups[gnums[i]];\r\n        //获得这次匹配的组 \r\n        Console.WriteLine(\"Group\" + gnums[i] + \"=[\" + g.ToString() + \"]\");\r\n        //计算这个组的起始位置和长度 \r\n        CaptureCollection cc = g.Captures;\r\n        for (int j = 0; j &lt; cc.Count; j++)\r\n        {\r\n            Capture c = cc[j];\r\n            Console.WriteLine(\" Capture\" + j + \"=[\" + c.ToString()\r\n              + \"] Index=\" + c.Index + \" Length=\" + c.Length);\r\n        }\r\n    }\r\n    //下一个匹配 \r\n    m = m.NextMatch();\r\n}</pre>\r\n<p>这个例子的输出结果如下：</p>\r\n<pre>abracadabra\r\nGroup1=[abra]\r\n Capture0=[abracad] Index=0 Length=7\r\n Capture1=[abra] Index=7 Length=4\r\nGroup2=[cad]\r\n Capture0=[cad] Index=4 Length=3\r\nGroup1=[abra]\r\n Capture0=[abracad] Index=12 Length=7\r\n Capture1=[abra] Index=19 Length=4\r\nGroup2=[cad]\r\n Capture0=[cad] Index=16 Length=3\r\nGroup1=[abra]\r\n Capture0=[abracad] Index=24 Length=7\r\n Capture1=[abra] Index=31 Length=4\r\nGroup2=[cad]\r\n Capture0=[cad] Index=28 Length=3</pre>\r\n<p><span style=\"white-space: pre;\"> </span>我们首先从考查字符串pat开始，pat中包含有表达式。第一个capture是从第一个圆括号开始的，然后表达式将匹配到一个abra。第二个capture组从第二个圆括号开始，但第一个capture组还没有结束，这意味着第一个组匹配的结果是abracad ，而第二个组的匹配结果仅仅是cad。因此如果通过使用？符号而使cad成为一项可选的匹配，匹配的结果就可能是abra或abracad。然后，第一个组就会结束，通过指定+符号要求表达式进行多次匹配。</p>\r\n<p><span style=\"white-space: pre;\"> </span>现在我们来看看匹配过程中发生的情况。首先，通过调用Regex的构造方法建立表达式的一个实例，并在其中指定各种选项。在这个例子中，由于在表达式中有注释，因此选用了RegexOptions.IgnorePatternWhitespace选项，另外还使用了一些空格。打开RegexOptions.IgnorePatternWhitespace选项，表达式将会忽略注释和其中没有转义的空格。</p>\r\n<p><span style=\"white-space: pre;\"> </span>然后，取得表达式中定义的组的编号的清单。你当然可以显性地使用这些编号，在这里使用的是编程的方法。如果使用了命名的组，作为一种建立快速索引的途径这种方法也十分有效。按组号而不是按字符串名称引用组可以提供更快的访问速度。</p>\r\n<p><span style=\"white-space: pre;\"> </span>接下来是完成第一次匹配。通过一个循环测试当前的匹配是否成功，接下来是从group 1开始重复对组清单执行这一操作。在这个例子中没有使用group 0的原因是group 0是一个完全匹配的字符串，如果要通过收集全部匹配的字符串作为一个单一的字符串，就会用到group 0了。<br />其实：<br />&nbsp; &nbsp; &nbsp; Groups[0]等于$0 (整个表达式匹配的内容)<br />&nbsp; &nbsp; &nbsp; Groups[1]等于$1 (第一个子表达式匹配的内容)</p>\r\n<p><span style=\"white-space: pre;\"> </span>我们跟踪每个group中的CaptureCollection。通常情况下每次匹配、每个group中只能有一个capture，但本例中的Group1则有两个capture：Capture0和Capture1。如果你仅需要Group1的ToString，就会只得到abra，当然它也会与abracad匹配。组中ToString的值就是其CaptureCollection中最后一个Capture的值，这正是我们所需要的。如果你希望整个过程在匹配abra后结束，就应该从表达式中删除+符号，让regex引擎知道我们只需要对表达式进行匹配。</p>\r\n<p><strong>MatchEvaluator委托的使用：</strong></p>\r\n<p><span style=\"white-space: pre;\"> </span>表示在 Replace 方法操作过程中每当找到正则表达式匹配时都调用的方法。对替换方法（如 Regex.Replace(String, MatchEvaluator)）找到的每个匹配，可以使用 MatchEvaluator 委托方法来执行自定义验证或处理操作。 对于每个匹配的字符串，Replace 方法对表示匹配的 Match 对象调用 MatchEvaluator 委托方法。 委托方法执行您希望执行的任何处理并返回一个字符串，Replace 方法将用此字符串替换匹配的字符串。</p>\r\n<p><span style=\"white-space: pre;\"> </span>示例：下面的代码示例显示一个原始字符串，对该原始字符串中的每个单词进行匹配，将每个匹配项的第一个字符转换为大写，然后显示转换后的字符串。</p>\r\n<pre class=\"prettyprint\">using System;\r\nusing System.Text.RegularExpressions;\r\n\r\nclass RegExSample\r\n{\r\n    static string CapText(Match m)\r\n    {\r\n        // Get the matched string.\r\n        string x = m.ToString();\r\n        // If the first char is lower case...\r\n        if (char.IsLower(x[0]))\r\n        {\r\n            // Capitalize it.\r\n            return char.ToUpper(x[0]) + x.Substring(1, x.Length - 1);\r\n        }\r\n        return x;\r\n    }\r\n\r\n    static void Main()\r\n    {\r\n        string text = \"four score and seven years ago\";\r\n        System.Console.WriteLine(\"text=[\" + text + \"]\");\r\n        Regex rx = new Regex(@\"\\w+\");\r\n        string result = rx.Replace(text, new MatchEvaluator(RegExSample.CapText));\r\n        System.Console.WriteLine(\"result=[\" + result + \"]\");\r\n    }\r\n}</pre>','',1,1,'2011-11-06 00:43:51','2011-11-06 00:43:51',7,'正则表达式',NULL),(39,'Asp.net和ajax未捕获异常处理机制','aspnet-ajax-exception',1,'<p>Asp.net提供了如下几种错误处理机制，它们具有一定的优先级：Page_Error事件 &gt; Application_Error事件 &gt; &lt;customErrors&gt;配置项，下面分别进行介绍。</p>\r\n<p><strong>1：Page_Error事件</strong></p>\r\n<p>Page_Error事件提供一种在页面级别出现错误的捕获方法。</p>\r\n<pre class=\"prettyprint\">protected void Page_Error(object sender, EventArgs e)\r\n{\r\n    Exception objErr = Server.GetLastError().GetBaseException();\r\n    Response.Write(\"Error:\" + objErr.Message);\r\n    Server.ClearError();\r\n}</pre>\r\n<p>由于一个Web应用中通常会有很多页面，甚至达几百个页面，不可能每个页面都去实现这么一个方法，通常的做法是采用 BasePage 基类的方法。让这个基类先继承自System.Web.UI.Page类，再在该类中实现Page_Error方法，以后每添加一个页面，都直接从该类继承过来。</p>\r\n<p><strong>2：Application_Error事件</strong></p>\r\n<p>Page_Error事件的作用域只在该页面，他要求每个页面都要去实现这个方法，在适用性和性能方面都有折扣。而Global.asax文件定义了asp.net应用程序中的所有应用程序对象共有的方法、属性和事件，我们可以不采用BasePage的处理方式，在Global.asax里实现Application_Error事件并处理也可以。下面模仿BasePage类里的处理异常方法，实现如下：</p>\r\n<pre class=\"prettyprint\">/// &lt;summary&gt;\r\n/// 出错处理:写日志,导航到公共出错页面\r\n/// &lt;/summary&gt;\r\n/// &lt;param name=\"sender\"&gt;&lt;/param&gt;\r\n/// &lt;param name=\"e\"&gt;&lt;/param&gt;\r\nprotected void Application_Error(object sender, EventArgs e)\r\n{\r\n    if (Server.GetLastError() == null) \r\n        return;\r\n\r\n    Exception ex = Server.GetLastError().GetBaseException();\r\n    string error = this.DealException(ex);\r\n    DotNet.Common.Util.Logger.WriteFileLog(error,\r\n        HttpContext.Current.Request.PhysicalApplicationPath + \"LogFile\");\r\n    if (ex.InnerException != null)\r\n    {\r\n        error = this.DealException(ex);\r\n        DotNet.Common.Util.Logger.WriteFileLog(error,\r\n            HttpContext.Current.Request.PhysicalApplicationPath + \"LogFile\");\r\n    }\r\n    this.Server.ClearError();\r\n    this.Response.Redirect(\"/Error.aspx\");\r\n}\r\n\r\n/// &lt;summary&gt;\r\n/// 处理异常，用来将主要异常信息写入文本日志\r\n/// &lt;/summary&gt;\r\n/// &lt;param name=\"ex\"&gt;&lt;/param&gt;\r\n/// &lt;returns&gt;&lt;/returns&gt;\r\nprivate string DealException(Exception ex)\r\n{\r\n    this.Application[\"StackTrace\"] = ex.StackTrace;\r\n    this.Application[\"MessageError\"] = ex.Message;\r\n    this.Application[\"SourceError\"] = ex.Source;\r\n    this.Application[\"TargetSite\"] = ex.TargetSite.ToString();\r\n    string error = string.Format(\r\n        \"URl：{0}\\n引发异常的方法：{1}\\n错误信息：{2}\\n错误堆栈：{3}\\n\",\r\n        this.Request.RawUrl, ex.TargetSite, ex.Message, ex.StackTrace);\r\n    return error;\r\n}\r\n</pre>\r\n<p>上面方式的好处是，写一次代码，应用程序发生的大部分异常它都给你捕捉处理了。够省事！</p>\r\n<p><strong>3：&lt;customErrors&gt;配置项</strong></p>\r\n<p>配置文件web.config中的&lt;customErrors&gt; 配置节，可将重定向页指定为默认的错误页defaultRedirect或者根据引发的 HTTP 错误代码指定特定页。如果发生在应用程序以前的任一级别都未捕获到的错误，则显示这个自定义页。</p>\r\n<pre class=\"prettyprint\">&lt;customErrors mode=\"On\" defaultRedirect=\"~/ErrorHandling/ApplicationError.html\"&gt;\r\n    &lt;error statusCode=\"403\" redirect=\"NoAccess.htm\" /&gt;\r\n    &lt;error statusCode=\"404\" redirect=\"FileNotFound.htm\" /&gt;\r\n&lt;/customErrors&gt;</pre>\r\n<p>如果Application_Error和&lt;customerErrors&gt;同时存在，也存在执行顺序的问题。因为优先级 Application_Error事件&gt; &lt;customErrors&gt;配置项，所以发生应用程序级错误时，优先执行Application_Error事件中的代码，如果 Application_Error事件中调用了Server.ClearError()函数，&lt;customerErrors&gt;配置节中的 defaultRedirect不起作用，因为Exception已经被清除；如果Application_Error事件中没用调用了 Server.ClearError()函数，错误页会重新定位到defaultRedict指定的URL页面，为用户显示友好出错信息。</p>\r\n<p><strong>4：IHttpModule接口处理</strong></p>\r\n<p>以上的捕获程序，基本上已经能够处理大部分的错误异常了，但是，在asp.net程序调用线程进行异步处理的时候，容易发生在后台线程或线程池里抛出的异常并不能被以上的方法完全捕捉到，这时就需要如下方法：</p>\r\n<p>1）创建一个实现IHttpModule接口的类</p>\r\n<pre class=\"prettyprint\">using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Web;\r\nusing System.Web.UI;\r\nusing System.Web.UI.WebControls;\r\nusing System.Text;\r\nusing DotNet.Common.Util;\r\n\r\nnamespace DotNet.Common.WebForm\r\n{\r\n    /// &lt;summary&gt;\r\n    /// 通用未捕获异常处理 \r\n    /// &lt;/summary&gt;\r\n    public class AspNetUnhandledExceptionModule : IHttpModule\r\n    {\r\n        static object syncObj = new object();\r\n        static bool isInit = false;\r\n\r\n        public AspNetUnhandledExceptionModule()\r\n        {\r\n        }\r\n\r\n        public void Init(HttpApplication context)\r\n        {\r\n            lock (syncObj)\r\n            {\r\n                if (!isInit)\r\n                {\r\n                    AppDomain.CurrentDomain.UnhandledException += \r\n                        new UnhandledExceptionEventHandler(OnUnhandledException);\r\n                    isInit = true;\r\n                }\r\n            }\r\n        }\r\n\r\n        public void Dispose()\r\n        {\r\n        }\r\n\r\n        void OnUnhandledException(object o, UnhandledExceptionEventArgs e)\r\n        {\r\n            if (e.ExceptionObject == null) \r\n                return;\r\n\r\n            Exception ex = e.ExceptionObject as Exception;\r\n            string error = string.Format(\r\n                           \"引发异常的方法：{0}\\n错误信息：{1}\\n错误堆栈：{2}\\n\",\r\n                           ex.TargetSite, ex.Message, ex.StackTrace);\r\n            Logger.WriteFileLog(error, AppDomain.CurrentDomain.BaseDirectory + \"LogFile\");\r\n        }\r\n    }\r\n}</pre>\r\n<p>2）web.config节点配置</p>\r\n<pre class=\"prettyprint\">&lt;httpModules&gt;\r\n    &lt;add name=\"AspNetUnhandledExceptionModule\" \r\n        type=\"DotNet.Common.WebForm.AspNetUnhandledExceptionModule, DotNet.Common.WebForm\"&gt;\r\n    &lt;/add&gt;\r\n&lt;/httpModules&gt;</pre>\r\n<p>最后贴出测试代码</p>\r\n<pre class=\"prettyprint\">protected void Page_Load(object sender, EventArgs e)\r\n{\r\n    if (!IsPostBack)\r\n    {\r\n        System.Threading.ThreadPool.QueueUserWorkItem(\r\n            new System.Threading.WaitCallback(Test), null);\r\n    }\r\n}\r\nprotected void Test(object state)\r\n{\r\n    int[] numArr = new int[100];\r\n    numArr[100] = 100; //异常\r\n}</pre>\r\n<p>需要说明的是，通过线程或者线程池处理的程序，在发生异常时，每个线程都会有它自己独立的上下文，所以HttpContext对象应尽可能少地出现在异常处理阶段。</p>','',1,1,'2011-11-06 20:11:26','2011-11-06 20:11:26',7,'asp.net, 异常处理',NULL),(40,'C#全角半角转换函数','csharp-sbc-dbc-convert',1,'<p>如下的两个函数会把全角字符转换为半角字符和把半角字符转换为全角字符</p>\r\n<pre class=\"prettyprint\">/// &lt;summary&gt;\r\n/// 转全角的函数(SBC case)\r\n/// &lt;/summary&gt;\r\n/// &lt;param name=\"input\"&gt;任意字符串&lt;/param&gt;\r\n/// &lt;returns&gt;全角字符串&lt;/returns&gt;\r\n///&lt;remarks&gt;\r\n///全角空格为12288，半角空格为32\r\n///其他字符半角(33-126)与全角(65281-65374)的对应关系是：均相差65248\r\n///&lt;/remarks&gt;\r\npublic static string ToSBC(string input)\r\n{\r\n    //半角转全角：\r\n    char[] c = input.ToCharArray();\r\n    for (int i = 0; i &lt; c.Length; i++)\r\n    {\r\n        if (c[i] == 32)\r\n        {\r\n            c[i] = (char)12288;\r\n            continue;\r\n        }\r\n        if (c[i] &lt; 127)\r\n            c[i] = (char)(c[i] + 65248);\r\n    }\r\n    return new string(c);\r\n}\r\n\r\n\r\n/// &lt;summary&gt; 转半角的函数(DBC case) &lt;/summary&gt;\r\n/// &lt;param name=\"input\"&gt;任意字符串&lt;/param&gt;\r\n/// &lt;returns&gt;半角字符串&lt;/returns&gt;\r\n///&lt;remarks&gt;\r\n///全角空格为12288，半角空格为32\r\n///其他字符半角(33-126)与全角(65281-65374)的对应关系是：均相差65248\r\n///&lt;/remarks&gt;\r\npublic static string ToDBC(string input)\r\n{\r\n    char[] c = input.ToCharArray();\r\n    for (int i = 0; i &lt; c.Length; i++)\r\n    {\r\n        if (c[i] == 12288)\r\n        {\r\n            c[i] = (char)32;\r\n            continue;\r\n        }\r\n        if (c[i] &gt; 65280 &amp;&amp; c[i] &lt; 65375)\r\n            c[i] = (char)(c[i] - 65248);\r\n    }\r\n    return new string(c);\r\n}</pre>','',1,1,'2011-11-06 20:50:44','2011-11-06 20:50:44',7,'',NULL),(41,'Windows下使用Python2.5与Django','windows-django-python25',1,'<p>在国内因为GAE被墙了，所以一开始并不在意所使用的Python的版本，一直在使用Python2.7，最近人在日本，发现appspot是可以直接访问的（咱中国什么时候也能像其它地方一样别搞那么多的管制）。于是就有了想发布一个网站的想法，重新学习了一把GAE，要学习的第一个条件就是需要Python版本是2.5的。于是折腾了一个晚上，终于搞定了。</p>\r\n<p>本人使用的OS为Windows7，本来一直在用Ubuntu，自从升级到11.10后，RabbitVCS又不能用了，原因是RabbitVCS目前还不支持Gnome3。</p>\r\n<p><strong>1. 安装Python2.5</strong></p>\r\n<p>到http://www.python.org/ftp/python/2.5/python-2.5.msi下载安装文件，直接安装。</p>\r\n<p><strong>2. 安装setuptools</strong></p>\r\n<p>这绝对是个好东西，要安装什么第三方库什么的，太方便了，到http://pypi.python.org/pypi/setuptools下载与Python2.5相匹配的setuptools-0.6c11.win32-py2.5.exe文件。</p>\r\n<p>安装好后，可以先设计环境变量， 新建系统变量Python25，值为&ldquo;C:\\Python25;C:\\Python25\\Scripts&rdquo;，中间用分号隔开，再修改&ldquo;Path&rdquo;变量，在Path变量最前面添加&ldquo;%Python25%;&rdquo;，别忘了分号。</p>\r\n<p>这样就可以安装其它的工具了，如django，whoosh等</p>\r\n<pre class=\"prettyprint\">easy_install django\r\neasy_install whoosh</pre>\r\n<p><strong> 3. 安装MySQL驱动</strong></p>\r\n<p>如果用的是Python2.5或之前的版本，最好到http://sourceforge.net/projects/mysql-python/files/mysql-python/进行下载，选择1.2.2，再选择&ldquo;MySQL-python-1.2.2.win32-py2.5.exe&rdquo;，如果用的是Python2.6或以上的版本就到http://code.google.com/p/soemin/downloads/list进行下载。</p>\r\n<p><strong>4. 安装PIL</strong></p>\r\n<p>到http://www.pythonware.com/products/pil/下载适合的版本，我选择了&ldquo;Python Imaging Library 1.1.7 for Python 2.5&rdquo;</p>\r\n<p><strong>5. 安装Apache服务器</strong></p>\r\n<p>到http://httpd.apache.org/download.cgi#apache22下载最新的版本。我选择了&ldquo;httpd-2.2.21-win32-x86-no_ssl.msi&rdquo;</p>\r\n<p><strong>6. 安装与配置mod_python</strong></p>\r\n<p>从http://archive.apache.org/dist/httpd/modpython/win/3.3.1/选择适合的版本，我选择了&ldquo;mod_python-3.3.1.win32-py2.5-Apache2.2.exe&rdquo;。</p>\r\n<p>注意：mod_python在Python2.5之后就没有更新了，如果使用的Python2.6或以上版本的话请使用mod_wsgi，如何配置请参考<a href=\"windows-deploy-django.html\">Windows下部署Django站点</a></p>\r\n<p>下载好安装文件后，如果是Windows7的话，请一定要用管理员身份打开，否则mod_python.so文件拷贝不到Apache的modules目录下。在安装过程中会弹出目录选择框选择Apache的安装目录，默认为&ldquo;C:\\Program Files\\Apache Software Foundation\\Apache2.2&rdquo;。<br />1）配置httpd.conf文件。在该文件中添加&ldquo;LoadModule python_module modules/mod_python.so&rdquo;，并在文件的最后添加Include conf/httpd.xxxx.conf，文件名随便取。这主要是为了不过多的操作默认的httpd.conf文件，把一些配置分离出去，便于管理。<br />2）配置httpd.xxxx.conf文件。该文件的内容如下：</p>\r\n<pre class=\"prettyprint\">&lt;Location \"/\"&gt;\r\n    SetHandler python-program\r\n    PythonHandler django.core.handlers.modpython\r\n    SetEnv DJANGO_SETTINGS_MODULE senbzblog.settings\r\n    PythonDebug On\r\n    PythonPath \"[\'D:/workspace/senbzblog\'] + sys.path\"\r\n&lt;/Location&gt;</pre>\r\n<p><span style=\"color: #ff00ff;\">Location</span>就是最终在URL上反映的地址，上的最终地址会是\"http://localhost/.......\"，<span style=\"color: #ff00ff;\">senbzblog.settings</span>是Django项目名加上settings.py文件名。此处设置不当会出现 ImportError: Could not import settings \"xxx.settings\" (Is it on sys.path? Does it have syntax errors?): No module named xxx.settings 错误。<span style=\"color: #ff00ff;\">\'D:/workspace/senbzblog\'</span>是项目的父目录，项目所有的上一层目录。</p>\r\n<p>至此，重启Apache应该就可以打开网页了。</p>\r\n<p>这于这个项目是从Python2.7转到Python2.5的，会出现一些兼容的问题，例如：Python2.6内置json解释库，是 smiplejson。但是Python2.5，并没有内置，需要手动安装。我选择使用了python-json 3.4。</p>\r\n<p>python中的json解释库有好几个，其中不同版本有使用上的差异。<br />常用有 json-py 与smiplejson <br />两个包其中json-py 包含一个minjson,用法一样，只是import时 是 import minjson<br />两个用法上有差别, 但import语句一样</p>\r\n<pre class=\"prettyprint\">import json         # 都是如此import的。\r\nimport minjson\r\n# json-py库用法\r\njson.read( js_obj )\r\njson.write(py_obj)\r\n# json的minjson用法 \r\nminjson.read( js_obj )\r\nminjson.write(py_obj)\r\n# smiplejson  的用法\r\njson.loads(js_obj)   \r\njson.dumps(py_obj)</pre>\r\n<p>smiplejson地址：http://pypi.python.org/pypi/simplejson/<br />python-json地址：http://pypi.python.org/pypi/python-json&nbsp;</p>\r\n<p>&nbsp;</p>','',1,1,'2011-11-09 22:46:35','2011-11-09 22:46:35',3,'python',2),(42,'Python 自省指南','python-introspection',1,'<p>本文转自（<a href=\"http://www.ibm.com/developerworks/cn/linux/l-pyint/index1.html\">http://www.ibm.com/developerworks/cn/linux/l-pyint/index1.html</a>）</p>\r\n<p><strong>一 什么是自省？</strong></p>\r\n<p>在日常生活中，自省（introspection）是一种自我检查行为。自省是指对某人自身思想、情绪、动机和行为的检查。伟大的哲学家苏格拉底将生命中的大部分时间用于自我检查，并鼓励他的雅典朋友们也这样做。他甚至对自己作出了这样的要求：&ldquo;未经自省的生命不值得存在。&rdquo;</p>\r\n<p>在计算机编程中，自省是指这种能力：检查某些事物以确定它是什么、它知道什么以及它能做什么。自省向程序员提供了极大的灵活性和控制力。一旦您使用了支持自省的编程语言，就会产生类似这样的感觉：&ldquo;未经检查的对象不值得实例化。&rdquo;</p>\r\n<p>本文介绍了 Python 编程语言的自省能力。整个 Python 语言对自省提供了深入而广泛的支持。实际上，很难想象假如 Python 语言没有其自省特性是什么样子。在读完本文时，您应该能够非常轻松地洞察到自己 Python 对象的&ldquo;灵魂&rdquo;。</p>\r\n<p>在深入研究更高级的技术之前，我们尽可能用最普通的方式来研究 Python 自省。有些读者甚至可能会争论说：我们开始时所讨论的特性不应称之为&ldquo;自省&rdquo;。我们必须承认，它们是否属于自省的范畴还有待讨论。但从本文的主旨出发，我们所关心的只是找出有趣问题的答案。</p>\r\n<p>现在让我们以交互方式使用 Python 来开始研究。当我们从命令行启动 Python 时，就进入了 Python shell，在这里可以输入 Python 代码，而且立刻会从 Python 解释器获得响应。</p>\r\n<p><span style=\"color: #ff00ff;\">清单 1. 以交互方式启动 Python 解释器</span></p>\r\n<pre class=\"prettyprint\">$ python\r\nPython 2.2.2 (#1, Oct 28 2002, 17:22:19)\r\n[GCC 3.2 (Mandrake Linux 9.0 3.2-1mdk)] on linux2\r\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\r\n&gt;&gt;&gt;</pre>\r\n<p>在让 Python 运行起来，并看到 Python 提示符（&gt;&gt;&gt;）之后，您可能想知道 Python 能识别什么字。大多数编程语言都有保留字或关键字，这些字在该语言中有特殊的意义，Python 也不例外。您可能还注意到，Python 建议我们输入 help 以获取更多信息。也许我们可以向 Python 寻求一些关于关键字的帮助。</p>\r\n<p><strong>二 Python 的联机帮助实用程序</strong></p>\r\n<p>让我们按建议的那样，通过输入 help 来开始讨论，并观察它是否会向我们提供任何关于关键字的线索：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 2. 向 Python 寻求帮助</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; help\r\nType help() for interactive help, or help(object) for help about object.</pre>\r\n<p>因为我们不知道哪些对象可能包含关键字，所以不指定任何特定对象来尝试 help()：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 3. 启动帮助实用程序</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; help()\r\nWelcome to Python 2.2!  This is the online help utility.\r\nIf this is your first time using Python, you should definitely check out\r\nthe tutorial on the Internet at http://www.python.org/doc/tut/.\r\nEnter the name of any module, keyword, or topic to get help on writing\r\nPython programs and using Python modules.  To quit this help utility and\r\nreturn to the interpreter, just type \"quit\".\r\nTo get a list of available modules, keywords, or topics, type \"modules\",\r\n\"keywords\", or \"topics\".  Each module also comes with a one-line summary\r\nof what it does; to list the modules whose summaries contain a given word\r\nsuch as \"spam\", type \"modules spam\".\r\nhelp&gt;</pre>\r\n<p>现在，我们对此的理解似乎深入了些。让我们在 help 提示符下输入 keywords：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 4. 用 keywords 寻求帮助</span></p>\r\n<pre class=\"prettyprint\">help&gt; keywords\r\nHere is a list of the Python keywords.  Enter any keyword to get more help.\r\nand                 elif                global              or\r\nassert              else                if                  pass\r\nbreak               except              import              print\r\nclass               exec                in                  raise\r\ncontinue            finally             is                  return\r\ndef                 for                 lambda              try\r\ndel                 from                not                 while\r\nhelp&gt; quit\r\nYou are now leaving help and returning to the Python interpreter.\r\nIf you want to ask for help on a particular object directly from the\r\ninterpreter, you can type \"help(object)\".  Executing \"help(\'string\')\"\r\nhas the same effect as typing a particular string at the help&gt; prompt.\r\n&gt;&gt;&gt;</pre>\r\n<p>输入 help() 后，会看到一条欢迎消息和一些指示信息，接着是 help 提示符。在提示符下输入 keywords，则会看到一个 Python 关键字列表。我们已经获得了问题的答案，于是退出帮助实用程序，这时会看到一条简短的告别消息，并返回到 Python 提示符下。</p>\r\n<p>正如您从这个示例可以看到的，Python 的联机帮助实用程序会显示关于各种主题或特定对象的信息。帮助实用程序很有用，并确实利用了 Python 的自省能力。但仅仅使用帮助不会揭示帮助是如何获得其信息的。而且，因为本文的目的是揭示 Python 自省的所有秘密，所以我们必须迅速地跳出对帮助实用程序的讨论。</p>\r\n<p>在结束关于帮助的讨论之前，让我们用它来获得一个可用模块的列表。模块只是包含 Python 代码的文本文件，其名称后缀是 .py。如果在 Python 提示符下输入 help(\'modules\')，或在 help 提示符下输入 modules，则会看到一长列可用模块，类似于下面所示的部分列表。自己尝试它以观察您的系统中有哪些可用模块，并了解为什么会认为 Python 是&ldquo;自带电池&rdquo;的。</p>\r\n<p><span style=\"color: #ff00ff;\">清单 5. 部分可用模块的列表</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; help(\'modules\')\r\nPlease wait a moment while I gather a list of all available modules...\r\nBaseHTTPServer      cgitb               marshal             sndhdr\r\nBastion             chunk               math                socket\r\nCDROM               cmath               md5                 sre\r\nCGIHTTPServer       cmd                 mhlib               sre_compile\r\nCanvas              code                mimetools           sre_constants\r\n	&lt;...&gt;\r\nbisect              macpath             signal              xreadlines\r\ncPickle             macurl2path         site                xxsubtype\r\ncStringIO           mailbox             slgc (package)      zipfile\r\ncalendar            mailcap             smtpd\r\ncgi                 markupbase          smtplib\r\nEnter any module name to get more help.  Or, type \"modules spam\" to search\r\nfor modules whose descriptions contain the word \"spam\".\r\n&gt;&gt;&gt;</pre>\r\n<p><strong>三 sys 模块</strong></p>\r\n<p>sys 模块是提供关于 Python 本身的详尽内在信息的模块。通过导入模块，并用点（.）符号引用其内容（如变量、函数和类）来使用模块。sys 模块包含各种变量和函数，它们揭示了当前的 Python 解释器有趣的详细信息。让我们研究其中的一部分。我们要再次以交互方式运行 Python，并在 Python 命令提示符下输入命令。首先，我们将导入 sys 模块。然后，我们会输入 sys.executable 变量，它包含到 Python 解释器的路径：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 6. 导入 sys 模块</span></p>\r\n<pre class=\"prettyprint\">$ python\r\nPython 2.2.2 (#1, Oct 28 2002, 17:22:19)\r\n[GCC 3.2 (Mandrake Linux 9.0 3.2-1mdk)] on linux2\r\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\r\n&gt;&gt;&gt; import sys\r\n&gt;&gt;&gt; sys.executable\r\n\'/usr/local/bin/python\'</pre>\r\n<p>当输入一行只包含对象名称的代码时，Python 通过显示该对象的表示进行响应，对于简单对象，往往显示对象的值。在本例中，因为所显示的值是用引号括起来的，所以我们得到一条线索：sys.executable 可能是字符串对象。稍后，我们将研究确定对象类型的其它更精确的方法，但只在 Python 提示符下输入对象名称是一种迅速而又方便的自省形式。</p>\r\n<p>让我们研究 sys 模块其它一些有用的属性。</p>\r\n<p>platform 变量告诉我们现在处于什么操作系统上：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 7. sys.platform 属性</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; sys.platform\r\n\'linux2\'</pre>\r\n<p>在当前的 Python 中，版本以字符串和元组（元组包含对象序列）来表示：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 8. sys.version 和 sys.version_info 属性</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; sys.version\r\n\'2.2.2 (#1, Oct 28 2002, 17:22:19) \\n[GCC 3.2 (Mandrake Linux 9.0 3.2-1mdk)]\'\r\n&gt;&gt;&gt; sys.version_info\r\n(2, 2, 2, \'final\', 0)</pre>\r\n<p>maxint 变量反映了可用的最大整数值：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 9. sys.maxint 属性</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; sys.maxint\r\n2147483647</pre>\r\n<p>argv 变量是一个包含命令行参数的列表（如果参数被指定的话）。第一项 argv[0] 是所运行脚本的路径。当我们以交互方式运行 Python 时，这个值是空字符串：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 10. sys.argv 属性</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; sys.argv\r\n[\'\']</pre>\r\n<p>当运行其它 Python shell 时，如 PyCrust，会看到类似于下面的信息：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 11. 使用 PyCrust 时的 sys.argv 属性</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; sys.argv[0]\r\n\'/home/pobrien/Code/PyCrust/PyCrustApp.py\'</pre>\r\n<p>path 变量是模块搜索路径，Python 在导入期间将在其中的目录列表中寻找模块。最前面的空字符串 \'\' 是指当前目录：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 12. sys.path 属性</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; sys.path\r\n[\'\', \'/home/pobrien/Code\',\r\n\'/usr/local/lib/python2.2\',\r\n\'/usr/local/lib/python2.2/plat-linux2\',\r\n\'/usr/local/lib/python2.2/lib-tk\',\r\n\'/usr/local/lib/python2.2/lib-dynload\',\r\n\'/usr/local/lib/python2.2/site-packages\']</pre>\r\n<p>modules 变量是一个字典，它将当前已装入的所有模块的名称映射到模块对象。如您所见，缺省情况下，Python 装入一些特定的模块：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 13. sys.modules 属性</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; sys.modules\r\n{\'stat\': &lt;module \'stat\' from \'/usr/local/lib/python2.2/stat.pyc\'&gt;,\r\n\'__future__\': &lt;module \'__future__\' from \'/usr/local/lib/python2.2/__future__.pyc\'&gt;,\r\n\'copy_reg\': &lt;module \'copy_reg\' from \'/usr/local/lib/python2.2/copy_reg.pyc\'&gt;,\r\n\'posixpath\': &lt;module \'posixpath\' from \'/usr/local/lib/python2.2/posixpath.pyc\'&gt;,\r\n\'UserDict\': &lt;module \'UserDict\' from \'/usr/local/lib/python2.2/UserDict.pyc\'&gt;,\r\n\'signal\': &lt;module \'signal\' (built-in)&gt;,\r\n\'site\': &lt;module \'site\' from \'/usr/local/lib/python2.2/site.pyc\'&gt;,\r\n\'__builtin__\': &lt;module \'__builtin__\' (built-in)&gt;,\r\n\'sys\': &lt;module \'sys\' (built-in)&gt;,\r\n\'posix\': &lt;module \'posix\' (built-in)&gt;,\r\n\'types\': &lt;module \'types\' from \'/usr/local/lib/python2.2/types.pyc\'&gt;,\r\n\'__main__\': &lt;module \'__main__\' (built-in)&gt;,\r\n\'exceptions\': &lt;module \'exceptions\' (built-in)&gt;,\r\n\'os\': &lt;module \'os\' from \'/usr/local/lib/python2.2/os.pyc\'&gt;,\r\n\'os.path\': &lt;module \'posixpath\' from \'/usr/local/lib/python2.2/posixpath.pyc\'&gt;}</pre>\r\n<p><strong>四 keyword 模块</strong></p>\r\n<p>让我们返回到关于 Python 关键字的问题。尽管帮助向我们显示了关键字列表，但事实证明一些帮助信息是硬编码的。关键字列表恰好是硬编码的，但毕竟它的自省程度不深。让我们研究一下，能否直接从 Python 标准库的某个模块中获取这个信息。如果在 Python 提示符下输入 help(\'modules keywords\')，则会看到如下信息：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 14. 同时使用 modules 和 keywords 寻求帮助</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; help(\'modules keywords\')\r\nHere is a list of matching modules.  Enter any module name to get more help.\r\nkeyword - Keywords (from \"graminit.c\")</pre>\r\n<p>看起来，keyword 模块好象包含关键字。在文本编辑器中打开 keyword.py 文件，我们可以看到，Python 确实可以把关键字列表显式地用作 keyword 模块的 kwlist 属性。在 keyword 模块的注释中，我们还可以看到，该模块是根据 Python 本身的源代码自动生成的，这可以保证其关键字列表是准确而完整的：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 15. keyword 模块的关键字列表</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; import keyword\r\n&gt;&gt;&gt; keyword.kwlist\r\n[\'and\', \'assert\', \'break\', \'class\', \'continue\', \'def\', \'del\', \'elif\', \'else\',\r\n\'except\', \'exec\', \'finally\', \'for\', \'from\', \'global\', \'if\', \'import\', \'in\', \'is\',\r\n\'lambda\', \'not\', \'or\', \'pass\', \'print\', \'raise\', \'return\', \'try\', \'while\', \'yield\']</pre>\r\n<p><strong> 五 dir() 函数</strong></p>\r\n<p>尽管查找和导入模块相对容易，但要记住每个模块包含什么却不是这么简单。您并不希望总是必须查看源代码来找出答案。幸运的是，Python 提供了一种方法，可以使用内置的 dir() 函数来检查模块（以及其它对象）的内容。</p>\r\n<p><span style=\"color: #ff00ff;\">清单 16. keyword 模块的属性</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; dir(keyword)\r\n[\'__all__\', \'__builtins__\', \'__doc__\', \'__file__\', \'__name__\',\r\n\'iskeyword\', \'keyword\', \'kwdict\', \'kwlist\', \'main\']</pre>\r\n<p>那么将它应用于我们先前讨论的 sys 模块会怎么样呢？</p>\r\n<p><span style=\"color: #ff00ff;\">清单 17. sys 模块的属性</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; dir(sys)\r\n[\'__displayhook__\', \'__doc__\', \'__excepthook__\', \'__name__\', \'__stderr__\',\r\n\'__stdin__\', \'__stdout__\', \'_getframe\', \'argv\', \'builtin_module_names\',\r\n\'byteorder\', \'copyright\', \'displayhook\', \'exc_info\', \'exc_type\', \'excepthook\',\r\n\'exec_prefix\', \'executable\', \'exit\', \'getdefaultencoding\', \'getdlopenflags\',\r\n\'getrecursionlimit\', \'getrefcount\', \'hexversion\', \'last_traceback\',\r\n\'last_type\', \'last_value\', \'maxint\', \'maxunicode\', \'modules\', \'path\',\r\n\'platform\', \'prefix\', \'ps1\', \'ps2\', \'setcheckinterval\', \'setdlopenflags\',\r\n\'setprofile\', \'setrecursionlimit\', \'settrace\', \'stderr\', \'stdin\', \'stdout\',\r\n\'version\', \'version_info\', \'warnoptions\']</pre>\r\n<p>如果不带任何参数，则 dir() 返回当前作用域中的名称。请注意，因为我们先前导入了 keyword 和 sys，所以它们出现在列表中。导入模块将把该模块的名称添加到当前作用域：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 18. 当前作用域中的名称</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; dir()\r\n[\'__builtins__\', \'__doc__\', \'__name__\', \'keyword\', \'sys\']</pre>\r\n<p>我们曾经提到 dir() 函数是内置函数，这意味着我们不必为了使用该函数而导入模块。不必做任何操作，Python 就可识别内置函数。现在，我们看到调用 dir() 后返回了这个名称 __builtins__。也许此处有连接。让我们在 Python 提示符下输入名称 __builtins__，并观察 Python 是否会告诉我们关于它的任何有趣的事情：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 19. __builtins__ 是什么？</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; __builtins__\r\n&lt;module \'__builtin__\' (built-in)&gt;</pre>\r\n<p>因此 __builtins__ 看起来象是当前作用域中绑定到名为 __builtin__ 的模块对象的名称。（因为模块不是只有多个单一值的简单对象，所以 Python 改在尖括号中显示关于模块的信息。）注：如果您在磁盘上寻找 __builtin__.py 文件，将空手而归。这个特殊的模块对象是 Python 解释器凭空创建的，因为它包含着解释器始终可用的项。尽管看不到物理文件，但我们仍可以将 dir() 函数应用于这个对象，以观察所有内置函数、错误对象以及它所包含的几个杂项属性。</p>\r\n<p><span style=\"color: #ff00ff;\">清单 20. __builtins__ 模块的属性</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; dir(__builtins__)\r\n[\'ArithmeticError\', \'AssertionError\', \'AttributeError\', \'DeprecationWarning\',\r\n\'EOFError\', \'Ellipsis\', \'EnvironmentError\', \'Exception\', \'False\',\r\n\'FloatingPointError\', \'IOError\', \'ImportError\', \'IndentationError\',\r\n\'IndexError\', \'KeyError\', \'KeyboardInterrupt\', \'LookupError\', \'MemoryError\',\r\n\'NameError\', \'None\', \'NotImplemented\', \'NotImplementedError\', \'OSError\',\r\n\'OverflowError\', \'OverflowWarning\', \'ReferenceError\', \'RuntimeError\',\r\n\'RuntimeWarning\', \'StandardError\', \'StopIteration\', \'SyntaxError\',\r\n\'SyntaxWarning\', \'SystemError\', \'SystemExit\', \'TabError\', \'True\', \'TypeError\',\r\n\'UnboundLocalError\', \'UnicodeError\', \'UserWarning\', \'ValueError\', \'Warning\',\r\n\'ZeroDivisionError\', \'_\', \'__debug__\', \'__doc__\', \'__import__\', \'__name__\',\r\n\'abs\', \'apply\', \'bool\', \'buffer\', \'callable\', \'chr\', \'classmethod\', \'cmp\',\r\n\'coerce\', \'compile\', \'complex\', \'copyright\', \'credits\', \'delattr\', \'dict\',\r\n\'dir\', \'divmod\', \'eval\', \'execfile\', \'exit\', \'file\', \'filter\', \'float\',\r\n\'getattr\', \'globals\', \'hasattr\', \'hash\', \'help\', \'hex\', \'id\', \'input\', \'int\',\r\n\'intern\', \'isinstance\', \'issubclass\', \'iter\', \'len\', \'license\', \'list\',\r\n\'locals\', \'long\', \'map\', \'max\', \'min\', \'object\', \'oct\', \'open\', \'ord\', \'pow\',\r\n\'property\', \'quit\', \'range\', \'raw_input\', \'reduce\', \'reload\', \'repr\', \'round\',\r\n\'setattr\', \'slice\', \'staticmethod\', \'str\', \'super\', \'tuple\', \'type\', \'unichr\',\r\n\'unicode\', \'vars\', \'xrange\', \'zip\']</pre>\r\n<p>dir() 函数适用于所有对象类型，包括字符串、整数、列表、元组、字典、函数、定制类、类实例和类方法。让我们将 dir() 应用于字符串对象，并观察 Python 返回什么。如您所见，即使简单的 Python 字符串也有许多属性：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 21. 字符串属性</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; dir(\'this is a string\')\r\n[\'__add__\', \'__class__\', \'__contains__\', \'__delattr__\', \'__doc__\', \'__eq__\',\r\n\'__ge__\', \'__getattribute__\', \'__getitem__\', \'__getslice__\', \'__gt__\',\r\n\'__hash__\', \'__init__\', \'__le__\', \'__len__\', \'__lt__\', \'__mul__\', \'__ne__\',\r\n\'__new__\', \'__reduce__\', \'__repr__\', \'__rmul__\', \'__setattr__\', \'__str__\',\r\n\'capitalize\', \'center\', \'count\', \'decode\', \'encode\', \'endswith\', \'expandtabs\',\r\n\'find\', \'index\', \'isalnum\', \'isalpha\', \'isdigit\', \'islower\', \'isspace\',\r\n\'istitle\', \'isupper\', \'join\', \'ljust\', \'lower\', \'lstrip\', \'replace\', \'rfind\',\r\n\'rindex\', \'rjust\', \'rstrip\', \'split\', \'splitlines\', \'startswith\', \'strip\',\r\n\'swapcase\', \'title\', \'translate\', \'upper\', \'zfill\']</pre>\r\n<p>自己尝试下列示例以观察它们返回什么。注：# 字符标记注释的开始。Python 将忽略从注释开始部分到该行结束之间的所有内容：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 22. 将 dir() 运用于其它对象</span></p>\r\n<pre class=\"prettyprint\">dir(42)   # Integer (and the meaning of life)\r\ndir([])   # List (an empty list, actually)\r\ndir(())   # Tuple (also empty)\r\ndir({})   # Dictionary (ditto)\r\ndir(dir)  # Function (functions are also objects)</pre>\r\n<p>为了说明 Python 自省能力的动态本质，让我们研究将 dir() 运用于定制类和一些类实例的示例。我们将以交互方式定义自己的类，创建一些类的实例，仅向其中一个实例添加唯一的属性，并观察 Python 能否一直保存所有这些。以下是结果：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 23. 将 dir() 运用于定制类、类实例和属性</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; class Person(object):\r\n...     \"\"\"Person class.\"\"\"\r\n...     def __init__(self, name, age):\r\n...         self.name = name\r\n...         self.age = age\r\n...     def intro(self):\r\n...         \"\"\"Return an introduction.\"\"\"\r\n...         return \"Hello, my name is %s and I\'m %s.\" % (self.name, self.age)\r\n...\r\n&gt;&gt;&gt; bob = Person(\"Robert\", 35)   # Create a Person instance\r\n&gt;&gt;&gt; joe = Person(\"Joseph\", 17)   # Create another\r\n&gt;&gt;&gt; joe.sport = \"football\"       # Assign a new attribute to one instance\r\n&gt;&gt;&gt; dir(Person)      # Attributes of the Person class\r\n[\'__class__\', \'__delattr__\', \'__dict__\', \'__doc__\', \'__getattribute__\',\r\n\'__hash__\', \'__init__\', \'__module__\', \'__new__\', \'__reduce__\', \'__repr__\',\r\n\'__setattr__\', \'__str__\', \'__weakref__\', \'intro\']\r\n&gt;&gt;&gt; dir(bob)         # Attributes of bob\r\n[\'__class__\', \'__delattr__\', \'__dict__\', \'__doc__\', \'__getattribute__\',\r\n\'__hash__\', \'__init__\', \'__module__\', \'__new__\', \'__reduce__\', \'__repr__\',\r\n\'__setattr__\', \'__str__\', \'__weakref__\', \'age\', \'intro\', \'name\']\r\n&gt;&gt;&gt; dir(joe)         # Note that joe has an additional attribute\r\n[\'__class__\', \'__delattr__\', \'__dict__\', \'__doc__\', \'__getattribute__\',\r\n\'__hash__\', \'__init__\', \'__module__\', \'__new__\', \'__reduce__\', \'__repr__\',\r\n\'__setattr__\', \'__str__\', \'__weakref__\', \'age\', \'intro\', \'name\', \'sport\']\r\n&gt;&gt;&gt; bob.intro()      # Calling bob\'s intro method\r\n\"Hello, my name is Robert and I\'m 35.\"\r\n&gt;&gt;&gt; dir(bob.intro)   # Attributes of the intro method\r\n[\'__call__\', \'__class__\', \'__cmp__\', \'__delattr__\', \'__doc__\', \'__get__\',\r\n\'__getattribute__\', \'__hash__\', \'__init__\', \'__new__\', \'__reduce__\',\r\n\'__repr__\', \'__setattr__\', \'__str__\', \'im_class\', \'im_func\', \'im_self\']</pre>\r\n<p><strong> 六 文档字符串</strong></p>\r\n<p>在许多 dir() 示例中，您可能会注意到的一个属性是 __doc__ 属性。这个属性是一个字符串，它包含了描述对象的注释。Python 称之为文档字符串或 docstring，以下是其工作原理。如果模块、类、方法或函数定义的第一条语句是字符串，那么该字符串会作为对象的 __doc__ 属性与该对象关联起来。例如，看一下 __builtins__ 对象的文档字符串。因为文档字符串通常包含嵌入的换行 \\n，我们将使用 Python 的 print 语句，以便输出更易于阅读：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 24. 模块文档字符串</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; print __builtins__.__doc__   # Module docstring\r\nBuilt-in functions, exceptions, and other objects.\r\nNoteworthy: None is the `nil\' object; Ellipsis represents `...\' in slices.</pre>\r\n<p>Python 甚至再次维持了在 Python shell 中以交互方式定义的类和方法上的文档字符串。让我们研究 Person 类及其 intro 方法的文档字符串：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 25. 类和方法文档字符串</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; Person.__doc__         # Class docstring\r\n\'Person class.\'\r\n&gt;&gt;&gt; Person.intro.__doc__   # Class method docstring\r\n\'Return an introduction.\'</pre>\r\n<p>因为文档字符串提供了如此有价值的信息，所以许多 Python 开发环境都有自动显示对象的文档字符串的方法。让我们再看一个 dir() 函数的文档字符串：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 26. 函数文档字符串</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; print dir.__doc__   # Function docstring\r\ndir([object]) -&gt; list of strings\r\nReturn an alphabetized list of names comprising (some of) the attributes\r\nof the given object, and of attributes reachable from it:\r\nNo argument:  the names in the current scope.\r\nModule object:  the module attributes.\r\nType or class object:  its attributes, and recursively the attributes of\r\n    its bases.\r\nOtherwise:  its attributes, its class\'s attributes, and recursively the\r\n    attributes of its class\'s base classes.</pre>\r\n<p><strong>七 检查 Python 对象</strong></p>\r\n<p>我们好几次提到了&ldquo;对象（object）&rdquo;这个词，但一直没有真正定义它。编程环境中的对象很象现实世界中的对象。实际的对象有一定的形状、大小、重量和其它特征。实际的对象还能够对其环境进行响应、与其它对象交互或执行任务。计算机中的对象试图模拟我们身边现实世界中的对象，包括象文档、日程表和业务过程这样的抽象对象。</p>\r\n<p>类似于实际的对象，几个计算机对象可能共享共同的特征，同时保持它们自己相对较小的变异特征。想一想您在书店中看到的书籍。书籍的每个物理副本都可能有污迹、几张破损的书页或唯一的标识号。尽管每本书都是唯一的对象，但都拥有相同标题的每本书都只是原始模板的实例，并保留了原始模板的大多数特征。</p>\r\n<p>对于面向对象的类和类实例也是如此。例如，可以看到每个 Python 字符串都被赋予了一些属性，dir() 函数揭示了这些属性。在前一个示例中，我们定义了自己的 Person 类，它担任创建个别 Person 实例的模板，每个实例都有自己的 name 和 age 值，同时共享自我介绍的能力。这就是面向对象。</p>\r\n<p>于是在计算机术语中，对象是拥有标识和值的事物，属于特定类型、具有特定特征和以特定方式执行操作。并且，对象从一个或多个父类继承了它们的许多属性。除了关键字和特殊符号（象运算符，如 +、-、*、**、/、%、&lt;、&gt; 等）外，Python 中的所有东西都是对象。Python 具有一组丰富的对象类型：字符串、整数、浮点、列表、元组、字典、函数、类、类实例、模块、文件等。</p>\r\n<p>当您有一个任意的对象（也许是一个作为参数传递给函数的对象）时，可能希望知道一些关于该对象的情况。在本节中，我们将向您展示如何让 Python 对象回答如下问题：</p>\r\n<ul>\r\n<li>对象的名称是什么？</li>\r\n<li>这是哪种类型的对象？</li>\r\n<li>对象知道些什么？</li>\r\n<li>对象能做些什么？</li>\r\n<li>对象的父对象是谁？</li>\r\n</ul>\r\n<p>1. 名称</p>\r\n<p>并非所有对象都有名称，但那些有名称的对象都将名称存储在其 __name__ 属性中。注：名称是从对象而不是引用该对象的变量中派生的。下面这个示例着重说明了这种区别：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 27. 名称中有什么？</span></p>\r\n<pre class=\"prettyprint\">$ python\r\nPython 2.2.2 (#1, Oct 28 2002, 17:22:19)\r\n[GCC 3.2 (Mandrake Linux 9.0 3.2-1mdk)] on linux2\r\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\r\n&gt;&gt;&gt; dir()                # The dir() function\r\n[\'__builtins__\', \'__doc__\', \'__name__\']\r\n&gt;&gt;&gt; directory = dir      # Create a new variable\r\n&gt;&gt;&gt; directory()          # Works just like the original object\r\n[\'__builtins__\', \'__doc__\', \'__name__\', \'directory\']\r\n&gt;&gt;&gt; dir.__name__         # What\'s your name?\r\n\'dir\'\r\n&gt;&gt;&gt; directory.__name__   # My name is the same\r\n\'dir\'\r\n&gt;&gt;&gt; __name__             # And now for something completely different\r\n\'__main__\'</pre>\r\n<p>模块拥有名称，Python 解释器本身被认为是顶级模块或主模块。当以交互的方式运行 Python 时，局部 __name__ 变量被赋予值 \'__main__\'。同样地，当从命令行执行 Python 模块，而不是将其导入另一个模块时，其 __name__ 属性被赋予值 \'__main__\' ，而不是该模块的实际名称。这样，模块可以查看其自身的 __name__ 值来自行确定它们自己正被如何使用，是作为另一个程序的支持，还是作为从命令行执行的主应用程序。因此，下面这条惯用的语句在 Python 模块中是很常见的：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 28. 用于执行或导入的测试</span></p>\r\n<pre class=\"prettyprint\">if __name__ == \'__main__\':\r\n    # Do something appropriate here, like calling a\r\n    # main() function defined elsewhere in this module.\r\n    main()\r\nelse:\r\n    # Do nothing. This module has been imported by another\r\n    # module that wants to make use of the functions,\r\n    # classes and other useful bits it has defined.</pre>\r\n<p>2. 类型</p>\r\n<p>type() 函数有助于我们确定对象是字符串还是整数，或是其它类型的对象。它通过返回类型对象来做到这一点，可以将这个类型对象与 types 模块中定义的类型相比较：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 29. 我是您的类型吗？</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; import types\r\n&gt;&gt;&gt; print types.__doc__\r\nDefine names for all type symbols known in the standard interpreter.\r\nTypes that are part of optional modules (e.g. array) are not listed.\r\n&gt;&gt;&gt; dir(types)\r\n[\'BufferType\', \'BuiltinFunctionType\', \'BuiltinMethodType\', \'ClassType\',\r\n\'CodeType\', \'ComplexType\', \'DictProxyType\', \'DictType\', \'DictionaryType\',\r\n\'EllipsisType\', \'FileType\', \'FloatType\', \'FrameType\', \'FunctionType\',\r\n\'GeneratorType\', \'InstanceType\', \'IntType\', \'LambdaType\', \'ListType\',\r\n\'LongType\', \'MethodType\', \'ModuleType\', \'NoneType\', \'ObjectType\', \'SliceType\',\r\n\'StringType\', \'StringTypes\', \'TracebackType\', \'TupleType\', \'TypeType\',\r\n\'UnboundMethodType\', \'UnicodeType\', \'XRangeType\', \'__builtins__\', \'__doc__\',\r\n\'__file__\', \'__name__\']\r\n&gt;&gt;&gt; s = \'a sample string\'\r\n&gt;&gt;&gt; type(s)\r\n&lt;type \'str\'&gt;\r\n&gt;&gt;&gt; if type(s) is types.StringType: print \"s is a string\"\r\n...\r\ns is a string\r\n&gt;&gt;&gt; type(42)\r\n&lt;type \'int\'&gt;\r\n&gt;&gt;&gt; type([])\r\n&lt;type \'list\'&gt;\r\n&gt;&gt;&gt; type({})\r\n&lt;type \'dict\'&gt;\r\n&gt;&gt;&gt; type(dir)\r\n&lt;type \'builtin_function_or_method\'&gt;</pre>\r\n<p>3. 标识</p>\r\n<p>先前我们说过，每个对象都有标识、类型和值。值得注意的是，可能有多个变量引用同一对象，同样地，变量可以引用看起来相似（有相同的类型和值），但拥有截然不同标识的多个对象。当更改对象时（如将某一项添加到列表），这种关于对象标识的概念尤其重要，如在下面的示例中，blist 和 clist 变量引用同一个列表对象。正如您在示例中所见，id() 函数给任何给定对象返回唯一的标识符：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 30. 目的地&hellip;&hellip;</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; print id.__doc__\r\nid(object) -&gt; integer\r\nReturn the identity of an object.  This is guaranteed to be unique among\r\nsimultaneously existing objects.  (Hint: it\'s the object\'s memory address.)\r\n&gt;&gt;&gt; alist = [1, 2, 3]\r\n&gt;&gt;&gt; blist = [1, 2, 3]\r\n&gt;&gt;&gt; clist = blist\r\n&gt;&gt;&gt; clist\r\n[1, 2, 3]\r\n&gt;&gt;&gt; blist\r\n[1, 2, 3]\r\n&gt;&gt;&gt; alist\r\n[1, 2, 3]\r\n&gt;&gt;&gt; id(alist)\r\n145381412\r\n&gt;&gt;&gt; id(blist)\r\n140406428\r\n&gt;&gt;&gt; id(clist)\r\n140406428\r\n&gt;&gt;&gt; alist is blist    # Returns 1 if True, 0 if False\r\n0\r\n&gt;&gt;&gt; blist is clist    # Ditto\r\n1\r\n&gt;&gt;&gt; clist.append(4)   # Add an item to the end of the list\r\n&gt;&gt;&gt; clist\r\n[1, 2, 3, 4]\r\n&gt;&gt;&gt; blist             # Same, because they both point to the same object\r\n[1, 2, 3, 4]\r\n&gt;&gt;&gt; alist             # This one only looked the same initially\r\n[1, 2, 3]</pre>\r\n<p>4. 属性</p>\r\n<p>我们已经看到对象拥有属性，并且 dir() 函数会返回这些属性的列表。但是，有时我们只想测试一个或多个属性是否存在。如果对象具有我们正在考虑的属性，那么通常希望只检索该属性。这个任务可以由 hasattr() 和 getattr() 函数来完成，如本例所示：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 31. 具有一个属性；获得一个属性</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; print hasattr.__doc__\r\nhasattr(object, name) -&gt; Boolean\r\nReturn whether the object has an attribute with the given name.\r\n(This is done by calling getattr(object, name) and catching exceptions.)\r\n&gt;&gt;&gt; print getattr.__doc__\r\ngetattr(object, name[, default]) -&gt; value\r\nGet a named attribute from an object; getattr(x, \'y\') is equivalent to x.y.\r\nWhen a default argument is given, it is returned when the attribute doesn\'t\r\nexist; without it, an exception is raised in that case.\r\n&gt;&gt;&gt; hasattr(id, \'__doc__\')\r\n1\r\n&gt;&gt;&gt; print getattr(id, \'__doc__\')\r\nid(object) -&gt; integer\r\nReturn the identity of an object.  This is guaranteed to be unique among\r\nsimultaneously existing objects.  (Hint: it\'s the object\'s memory address.)</pre>\r\n<p>5. 可调用</p>\r\n<p>可以调用表示潜在行为（函数和方法）的对象。可以用 callable() 函数测试对象的可调用性：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 32. 您能为我做些事情吗？</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; print callable.__doc__\r\ncallable(object) -&gt; Boolean\r\nReturn whether the object is callable (i.e., some kind of function).\r\nNote that classes are callable, as are instances with a __call__() method.\r\n&gt;&gt;&gt; callable(\'a string\')\r\n0\r\n&gt;&gt;&gt; callable(dir)\r\n1</pre>\r\n<p>6. 实例</p>\r\n<p>在 type() 函数提供对象的类型时，还可以使用 isinstance() 函数测试对象，以确定它是否是某个特定类型或定制类的实例：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 33. 您是那些实例中的一个吗？</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; print isinstance.__doc__\r\nisinstance(object, class-or-type-or-tuple) -&gt; Boolean\r\nReturn whether an object is an instance of a class or of a subclass thereof.\r\nWith a type as second argument, return whether that is the object\'s type.\r\nThe form using a tuple, isinstance(x, (A, B, ...)), is a shortcut for\r\nisinstance(x, A) or isinstance(x, B) or ... (etc.).\r\n&gt;&gt;&gt; isinstance(42, str)\r\n0\r\n&gt;&gt;&gt; isinstance(\'a string\', int)\r\n0\r\n&gt;&gt;&gt; isinstance(42, int)\r\n1\r\n&gt;&gt;&gt; isinstance(\'a string\', str)\r\n1</pre>\r\n<p>7 子类</p>\r\n<p>我们先前提到过，定制类的实例从该类继承了属性。在类这一级别，可以根据一个类来定义另一个类，同样地，这个新类会按照层次化的方式继承属性。Python 甚至支持多重继承，多重继承意味着可以用多个父类来定义一个类，这个新类继承了多个父类。issubclass() 函数使我们可以查看一个类是不是继承了另一个类：</p>\r\n<p><span style=\"color: #ff00ff;\">清单 34. 您是我母亲吗？</span></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; print issubclass.__doc__\r\nissubclass(C, B) -&gt; Boolean\r\nReturn whether class C is a subclass (i.e., a derived class) of class B.\r\n&gt;&gt;&gt; class SuperHero(Person):   # SuperHero inherits from Person...\r\n...     def intro(self):       # but with a new SuperHero intro\r\n...         \"\"\"Return an introduction.\"\"\"\r\n...         return \"Hello, I\'m SuperHero %s and I\'m %s.\" % (self.name, self.age)\r\n...\r\n&gt;&gt;&gt; issubclass(SuperHero, Person)\r\n1\r\n&gt;&gt;&gt; issubclass(Person, SuperHero)\r\n0\r\n&gt;&gt;&gt;</pre>\r\n<p><strong> 八 检查时间</strong></p>\r\n<p>让我们将上一节中讨论的几种检查技术结合起来。为了做到这一点，要定义自己的函数 &mdash; interrogate()，它打印有关传递给它的任何对象的各种信息。以下是代码，后面是其用法的几个示例：</p>\r\n<p>清单 35. 谁也没料到它</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; def interrogate(item):\r\n...     \"\"\"Print useful information about item.\"\"\"\r\n...     if hasattr(item, \'__name__\'):\r\n...         print \"NAME:    \", item.__name__\r\n...     if hasattr(item, \'__class__\'):\r\n...         print \"CLASS:   \", item.__class__.__name__\r\n...     print \"ID:      \", id(item)\r\n...     print \"TYPE:    \", type(item)\r\n...     print \"VALUE:   \", repr(item)\r\n...     print \"CALLABLE:\",\r\n...     if callable(item):\r\n...         print \"Yes\"\r\n...     else:\r\n...         print \"No\"\r\n...     if hasattr(item, \'__doc__\'):\r\n...         doc = getattr(item, \'__doc__\')\r\n... 	doc = doc.strip()   # Remove leading/trailing whitespace.\r\n... 	firstline = doc.split(\'\\n\')[0]\r\n... 	print \"DOC:     \", firstline\r\n...\r\n&gt;&gt;&gt; interrogate(\'a string\')     # String object\r\nCLASS:    str\r\nID:       141462040\r\nTYPE:     &lt;type \'str\'&gt;\r\nVALUE:    \'a string\'\r\nCALLABLE: No\r\nDOC:      str(object) -&gt; string\r\n&gt;&gt;&gt; interrogate(42)             # Integer object\r\nCLASS:    int\r\nID:       135447416\r\nTYPE:     &lt;type \'int\'&gt;\r\nVALUE:    42\r\nCALLABLE: No\r\nDOC:      int(x[, base]) -&gt; integer\r\n&gt;&gt;&gt; interrogate(interrogate)    # User-defined function object\r\nNAME:     interrogate\r\nCLASS:    function\r\nID:       141444892\r\nTYPE:     &lt;type \'function\'&gt;\r\nVALUE:    &lt;function interrogate at 0x86e471c&gt;\r\nCALLABLE: Yes\r\nDOC:      Print useful information about item.</pre>\r\n<p>正如您在最后一个示例中所看到的，interrogate() 函数甚至可以应用于它本身。您没有再比它更具&ldquo;自省性&rdquo;的工具了。</p>\r\n<p><strong>结束语</strong></p>\r\n<p>谁知道自省可以变得这么简单而又如此有价值？可是，我在结束时必须提出警告：不要将自省的结果误认为是万能的。有经验的 Python 程序员知道：他们不知道的东西总是比已知的更多，因此根本不可能是万能的。编程行为产生的问题多于答案。关于 Python 的唯一优点（正如我们今天在本文中所见）是它确实回答了人们的问题。至于我本人，觉得您不必因为我帮助您了解了这些 Python 必须提供的内容而酬谢我。用 Python 编程自有回报。我从爱好 Python 的同伴处获得的帮助也是不计报酬的。</p>','',1,1,'2011-12-25 00:19:57','2011-12-25 00:19:57',2,'python',NULL),(43,'Python字符串处理','python-string',1,'<p>在python有各种各样的string操作函数。在历史上string类在python中经历了一段轮回的历史。在最开始的时候，python有一个专门的string的module，要使用string的方法要先import，但后来由于众多的python使用者的建议，从python2.0开始， string方法改为用S.method()的形式调用，只要S是一个字符串对象就可以这样使用，而不用import。同时为了保持向后兼容，现在的 python中仍然保留了一个string的module，其中定义的方法与S.method()是相同的，这些方法都最后都指向了用S.method ()调用的函数。要注意，S.method()能调用的方法比string的module中的多，比如isdigit()、istitle()等就只能用 S.method()的方式调用。</p>\r\n<p>对一个字符串对象，首先想到的操作可能就是计算它有多少个字符组成，很容易想到用S.len()，但这是错的，应该是len(S)。因为len()是内置函数，包括在__builtin__模块中。python不把len()包含在string类型中，乍看起来好像有点不可理解，其实一切有其合理的逻辑在里头。len()不仅可以计算字符串中的字符数，还可以计算list的成员数，tuple的成员数等等，因此单单把len()算在string里是不合适，因此一是可以把len()作为通用函数，用重载实现对不同类型的操作，还有就是可以在每种有len()运算的类型中都要包含一个len()函数。 python选择的是第一种解决办法。类似的还有str(arg)函数，它把arg用string类型表示出来。</p>\r\n<p>字符串对象是不可改变的，也就是说在python创建一个字符串后，你不能把这个字符中的某一部分改变。任何上面的函数改变了字符串后，都会返回一个新的字符串，原字串并没有变。</p>\r\n<p><strong>1. 字符串中字符大小写的变换：</strong></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; s = \'the Great Wall\'    # 定义一个字符串\r\n&gt;&gt;&gt; s.lower()               # 全部转换为小写\r\n\'the great wall\'\r\n&gt;&gt;&gt; s.upper()               # 全部转换为大写\r\n\'THE GREAT WALL\'\r\n&gt;&gt;&gt; s.capitalize()          # 字符串首字母大写\r\n\'The great wall\'\r\n&gt;&gt;&gt; string.capwords(s)      # 模块中的方法，每个单词的首字母大写\r\n\'The Great Wall\'\r\n&gt;&gt;&gt; s.title()               # 每个单词的首字母大写，其余为小写，模块中没有这个方法\r\n\'The Great Wall\'</pre>\r\n<p>title()函数是比较特别的，它的功能是将每一个单词的首字母大写，并将单词中的非首字母转换为小写（英文文章的标题通常是这种格式）。因为title() 函数并不去除字符串两端的空白符也不会把连续的空白符替换为一个空格，所以建议使用string 模块中的capwords(s)函数，它能够去除两端的空白符，再将连续的空白符用一个空格代替。</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; \'hello wORld!\'.title()\r\n\'Hello World!\'\r\n&gt;&gt;&gt; \' hello   world!\'.title()\r\n\' Hello   World!\'\r\n&gt;&gt;&gt; string.capwords(\' hello   world!\')\r\n\'Hello World!\'</pre>\r\n<p><strong>2. 字符串在输出时的对齐：</strong></p>\r\n<p>直接以例子说明，看看下面的语句输出的结果</p>\r\n<pre class=\"prettyprint\">print \'|\', \'*\'.ljust(10), \'|\'\r\nprint \'|\', \'*\'.ljust(10, \'-\'), \'|\'\r\nprint \'|\', \'*\'.rjust(10, \'-\'), \'|\'\r\nprint \'|\', \'*\'.center(10, \'-\'), \'|\'\r\nprint \'|\', \'*\'.zfill(10), \'|\'\r\nprint \'|\', \'*\\t\\t\'.expandtabs(4), \'|\'</pre>\r\n<p>结果如下：</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; \r\n| *          |\r\n| *--------- |\r\n| ---------* |\r\n| ----*----- |\r\n| 000000000* |\r\n| *        |</pre>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td style=\"width: 150px;\">ljust(width[, fillchar])</td>\r\n<td>输出width个字符，*号左对齐，不足部分用fillchar填充，直到总长度满足width为止，默认的为空格。</td>\r\n</tr>\r\n<tr>\r\n<td>rjust(width[, fillchar])</td>\r\n<td>与ljust相反，为右对齐</td>\r\n</tr>\r\n<tr>\r\n<td>center(width[, fillchar])</td>\r\n<td>中间对齐，两边用fillchar填写，直到总长度满足width为止。</td>\r\n</tr>\r\n<tr>\r\n<td>zfill(width)</td>\r\n<td>顾名思义，zfill()即是以字符0进行填充，在输出数值时比较常用</td>\r\n</tr>\r\n<tr>\r\n<td>expandtabs([tabsize])</td>\r\n<td>expandtabs()的tabsize 参数默认为8。它的功能是把字符串中的制表符（tab）转换为适当数量的空格。</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p><strong>3. 字符串中的搜索与替换：</strong></p>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td style=\"width: 170px;\">find( sub[, start[, end]])</td>\r\n<td>返回字符串中出现sub的第一个字母的标号，如果字符串中没有sub则返回-1。如果指定了start和end，则表示从第start个字符位置开始到end个位置结果的字符串之间查找</td>\r\n</tr>\r\n<tr>\r\n<td>index( sub[, start[, end]])</td>\r\n<td>与find()相同，只是在字符串中没有sub时，会抛出ValueError异常</td>\r\n</tr>\r\n<tr>\r\n<td>rfind( sub[, start[,end]])</td>\r\n<td>返回字符串中最后出现的sub的第一个字母的标号，如果字符串中没有sub则返回-1，也就是说从右边算起的第一次出现的sub的首字母标号</td>\r\n</tr>\r\n<tr>\r\n<td>rindex( sub[, start[, end]])</td>\r\n<td>与rfind一样，只是找不到时抛出异常</td>\r\n</tr>\r\n<tr>\r\n<td>count( sub[, start[, end]])</td>\r\n<td>计算sub在字符串中出现的次数</td>\r\n</tr>\r\n<tr>\r\n<td>replace(old, new[,count])</td>\r\n<td>把字符串中的old替换为new，count为替换次数。这是替换的通用形式，还有一些函数进行特殊字符的替换</td>\r\n</tr>\r\n<tr>\r\n<td>translate(table[,deletechars])</td>\r\n<td>translate() 的参数 table 可以由 string.maketrans(frm, to) 生成， translate() 对 unicode 对象的支持并不完备，建议不要使用。</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>string.maketrans(from, to)返回一个256个字符组成的翻译表，其中from中的字符被一一对应地转换成to，所以from和to必须是等长的。translate(table[,deletechars])使用上面的string.maketrans函数产后的翻译表，把字符串进行翻译，并把deletechars中有的字符删掉。需要注意的是，如果对象字符串为unicode字符串，那么就不支持 deletechars参数，可以使用把某个字符翻译为None的方式实现相同的功能。此外还可以使用codecs模块的功能来创建更加功能强大的翻译表，请看如下实例：</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; import string\r\n&gt;&gt;&gt; s = \'abcdefg-1234567\'\r\n&gt;&gt;&gt; #没有映射，实际上就是按原始字符保留，看下面用到translate中时的效果\r\n&gt;&gt;&gt; table = string.maketrans(\'\', \'\')\r\n&gt;&gt;&gt; s.translate(table)  # 输出abcdefg-1234567\r\n\'abcdefg-1234567\'\r\n&gt;&gt;&gt; s.translate(table, \'abc123\') #输出defg-4567 可以看到删除了字符abc123\r\n\'defg-4567\'\r\n&gt;&gt;&gt; \r\n&gt;&gt;&gt; #下面再看有字符映射时的效果\r\n&gt;&gt;&gt; table = string.maketrans(\'abc\', \'ABC\') #用于translate中时的效果如下\r\n&gt;&gt;&gt; #输出ABCdefg-1234567 就是将abc映射为大写的ABC，前提是abc如果被保留下来了\r\n&gt;&gt;&gt; s.translate(table)\r\n\'ABCdefg-1234567\'\r\n&gt;&gt;&gt; #输出Cdefg-4567 先把s中的ab123去除了，\r\n&gt;&gt;&gt; #然后在保留下来的字符中应用table中指定的字符映射关系映射：c -&gt; C\r\n&gt;&gt;&gt; s.translate(table, \'ab123\')\r\n\'Cdefg-4567\'</pre>\r\n<p><strong>4. 除去字符串的空白：</strong></p>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td>strip([chars])</td>\r\n<td>strip()函数族用以去除字符串两端的空白符，空白符由string.whitespace常量定义。</td>\r\n</tr>\r\n<tr>\r\n<td>lstrip([chars])</td>\r\n<td>除去对象字符串中左边的chars中指定的字符</td>\r\n</tr>\r\n<tr>\r\n<td>rstrip([chars])</td>\r\n<td>除去对象字符串中右边的chars中指定的字符</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p><strong>5. 字符串的分割和组合：</strong></p>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td style=\"width: 150px;\">split([sep [,maxsplit]])</td>\r\n<td>以sep为分隔符，把对象字符串分成一个list。参数 maxsplit 是分切的次数，即最大的分切次数，所以返回值最多有 maxsplit+1 个元素。</td>\r\n</tr>\r\n<tr>\r\n<td>rsplit([sep [,maxsplit]])</td>\r\n<td>从右边开始分隔</td>\r\n</tr>\r\n<tr>\r\n<td>splitlines([keepends])</td>\r\n<td>把S按照行分割符分为一个list，keepends是一个bool值，如果为真每行后而会保留行分割符。</td>\r\n</tr>\r\n<tr>\r\n<td>partition(sep)</td>\r\n<td>*partition()函数族是2.5版本新增的方法。它接受一个字符串参数，并返回一个3个元素的 tuple 对象。如果sep没出现在母串中，返回值是 (sep, \'\', \'\')；否则，返回值的第一个元素是 sep 左端的部分，第二个元素是 sep 自身，第三个元素是 sep 右端的部分。</td>\r\n</tr>\r\n<tr>\r\n<td>rpartition(sep)</td>\r\n<td>同partition</td>\r\n</tr>\r\n<tr>\r\n<td>join(seq)</td>\r\n<td>把seq代表的序列──字符串序列，用对象字符串连接起来</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>s.split() 和 s.split(\' \')的返回值不尽相同</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; \' hello   world!\'.split()\r\n[\'hello\', \'world!\']\r\n&gt;&gt;&gt; \' hello   world!\'.split(\' \')\r\n[\'\', \'hello\', \'\', \'\', \'world!\']</pre>\r\n<p>产生差异的原因在于当忽略 sep 参数或sep参数为 None 时与明确给 sep 赋予字符串值时 split() 采用两种不同的算法。对于前者，split() 先去除字符串两端的空白符，然后以任意长度的空白符串作为界定符分切字符串（即连续的空白符串被当作单一的空白符看待）；对于后者则认为两个连续的 sep 之间存在一个空字符串。因此对于空字符串（或空白符串），它们的返回值也是不同的：</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; \'\'.split()\r\n[]\r\n&gt;&gt;&gt; \'\'.split(\' \')\r\n[\'\']</pre>\r\n<p>join() 函数的高效率（相对于循环相加而言），使它成为最值得关注的字符串方法之一。它的功用是将可迭代的字符串序列连接成一条长字符串，如：</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; conf = {\'host\':\'127.0.0.1\',\r\n	\'db\':\'spam\',\r\n	\'user\':\'sa\',\r\n	\'passwd\':\'eggs\'}\r\n&gt;&gt;&gt; \';\'.join(\"%s=%s\" % (k, v) for k, v in conf.iteritems())\r\n\'passwd=eggs;host=127.0.0.1;db=spam;user=sa\'</pre>\r\n<p><strong>6. 字符串还有一对编码和解码</strong></p>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td>encode([encoding[,errors]])</td>\r\n<td>其中encoding可以有多种值，比如gb2312 gbk gb18030 bz2 zlib big5 bzse64等都支持。errors默认值为\"strict\"，意思是UnicodeError。可能的值还有\'ignore\', \'replace\', \'xmlcharrefreplace\', \'backslashreplace\' 和所有的通过codecs.register_error注册的值。</td>\r\n</tr>\r\n<tr>\r\n<td>decode([encoding[,errors]])</td>\r\n<td>解码</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>这是一对互逆操作的方法，用以编码和解码字符串。因为str是平台相关的，它使用的内码依赖于操作系统环境，而unicode是平台无关的，是Python内部的字符串存储方式。unicode可以通过编码（encode）成为特定编码的str，而str也可以通过解码（decode）成为unicode。</p>\r\n<p><strong>7. 字符串的测试函数</strong></p>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td>islower()</td>\r\n<td>判断是否全是小写，只要有一个字符是大写就会返回False</td>\r\n</tr>\r\n<tr>\r\n<td>isupper()</td>\r\n<td>判断是否全是大写</td>\r\n</tr>\r\n<tr>\r\n<td>istitle()</td>\r\n<td>是否是首字母大写的</td>\r\n</tr>\r\n<tr>\r\n<td>isspace()</td>\r\n<td>是否全是空白字符，并至少有一个字符</td>\r\n</tr>\r\n<tr>\r\n<td>isdigit()</td>\r\n<td>是否全是数字，并至少有一个字符</td>\r\n</tr>\r\n<tr>\r\n<td>isalpha()</td>\r\n<td>是否全是字母，并至少有一个字符</td>\r\n</tr>\r\n<tr>\r\n<td>isalnum()</td>\r\n<td>是否全是字母和数字，并至少有一个字符</td>\r\n</tr>\r\n<tr>\r\n<td>startswith(prefix[, start[, end]])</td>\r\n<td>是否以prefix开头</td>\r\n</tr>\r\n<tr>\r\n<td>endswith(suffix[,start[, end]])</td>\r\n<td>是否以suffix结尾</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>这些函数都比较简单，顾名知义。需要注意的是*with()函数族可以接受可选的 start, end 参数，善加利用，可以优化性能。</p>\r\n<p>另，自 Py2.5 版本起，*with() 函数族的 prefix 参数可以接受 tuple 类型的实参，当实参中的某人元素能够匹配，即返回 True。</p>\r\n<p><strong>8. 字符串类型转换函数，这几个函数只在string模块中有：</strong></p>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td>string.atoi(s[,base])</td>\r\n<td>base默认是10，这个函数的作用是将字符串 s 转换成数值类型，而base的作用是指定进制</td>\r\n</tr>\r\n<tr>\r\n<td>string.atol(s[,base])</td>\r\n<td>转成long</td>\r\n</tr>\r\n<tr>\r\n<td>string.atof(s[,base])</td>\r\n<td>转成float</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p><strong>9. string与list互转</strong></p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; import string\r\n&gt;&gt;&gt; str = \'abcde\'\r\n&gt;&gt;&gt; list = list(str)\r\n&gt;&gt;&gt; list\r\n[\'a\', \'b\', \'c\', \'d\', \'e\']\r\n&gt;&gt;&gt; str\r\n\'abcde\'\r\n&gt;&gt;&gt; str_convert = \'\'.join(list)\r\n&gt;&gt;&gt; str_convert\r\n\'abcde\'</pre>\r\n<p>参考资料：<br /><a href=\"http://blog.csdn.net/lanphaday/article/details/2610733\">http://blog.csdn.net/lanphaday/article/details/2610733<br /></a><a href=\"http://bbs.linuxpk.com/archiver/tid-35329.html\">http://bbs.linuxpk.com/archiver/tid-35329.html</a>&nbsp;</p>','',1,1,'2011-12-27 22:43:03','2011-12-27 22:43:03',2,'python',NULL),(44,'str()和 repr()及 `` 操作符','python-str-repr',1,'<p>内建函数str()和repr()或反引号操作符（``）可以方便地以字符串的方式获取对象的内容、类型、数值属性等信息。str()函数得到的字符串可读性好，而repr()函数得到的字符串通常可以用来重新获得该对象，通常情况下obj == eval(repr(obj)) 这个等式是成立的。这两个函数接受一个对象作为其参数，返回适当的字符串。在下面的例子里，我们会随机取一些Python对象来查看他们的字符串表示。</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; str(4.53-2j)\r\n\'(4.53-2j)\'\r\n&gt;&gt;&gt;\r\n&gt;&gt;&gt; str(1)\r\n\'1\'\r\n&gt;&gt;&gt;\r\n&gt;&gt;&gt; str(2e10)\r\n\'20000000000.0\'\r\n&gt;&gt;&gt;\r\n&gt;&gt;&gt; str([0, 5, 9, 9])\r\n\'[0, 5, 9, 9]\'\r\n&gt;&gt;&gt;\r\n&gt;&gt;&gt; repr([0, 5, 9, 9])\r\n\'[0, 5, 9, 9]\'\r\n&gt;&gt;&gt;\r\n&gt;&gt;&gt; `[0, 5, 9, 9]`\r\n\'[0, 5, 9, 9]\'</pre>\r\n<p>尽管str(),repr()和``运算在特性和功能方面都非常相似，事实上repr()和``做的是完全一样的事情，它们返回的是一个对象的&ldquo;官方&rdquo;字符串表示，也就是说绝大多数情况下可以通过求值运算（使用内建函数eval()）重新得到该对象，但str()则有所不同。str()致力于生成一个对象的可读性好的字符串表示，它的返回结果通常无法用于eval()求值，但很适合用于print语句输出。需要再次提醒的是，并不是所有repr()返回的字符串都能够用 eval()内建函数得到原来的对象。</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; eval(`type(type))`) \r\nFile \"&lt;stdin&gt;\", line 1\r\neval(`type(type))`)\r\n^  \r\nSyntaxError：invalid syntax</pre>\r\n<p>也就是说 repr() 输出对 Python比较友好，而str()的输出对用户比较友好。虽然如此，很多情况下这三者的输出仍然都是完全一样的。</p>\r\n<p>为什么我们有了repr（）还需要``？</p>\r\n<p>在Python学习过程中，你偶尔会遇到某个操作符和某个函数是做同样一件事情。之所以如此是因为某些场合函数会比操作符更适合使用。举个例子，当处理类似函数这样的可执行对象或根据不同的数据项调用不同的函数处理时，函数就比操作符用起来方便。另一个例子就是双星号（＊＊）乘方运算和pow()内建函数，x ** y 和 pow(x,y) 执行的都是x的y次方（事实上Python社区目前已经不鼓励继续使用``操作符。）。</p>','',1,1,'2011-12-28 21:33:53','2011-12-28 21:33:53',2,'python',1),(45,'SQLPlus远程连接Oracle数据库','oracle-sqlplus-remote-connection',1,'<p>在服务器安装完Oracle数据库后，要想进行远程连接，需要有如下操作</p>\r\n<p>1. 设置防火墙</p>\r\n<p>需要关闭防火墙，或在防火墙中为Oracle添加例外</p>\r\n<p>2. 打开自带的SQL Plus程序进行连接</p>\r\n<p>命令：sqlplus 用户名/密码@ip地址[:端口]/service_name [as sysdba]</p>\r\n<p>示例：sqlplus sys/orcl@10.20.52.5/orcl as sysdba，使用默认1521端口时可省略输入</p>\r\n<p>3. 修改sqlnet.ora文件</p>\r\n<p>如果在第2步中出现错误：<span style=\"color: #ff00ff;\">ora-28547:连接服务器失败，可能是 Oracle Net 管理失败</span>；则需要这一步的修改操作。在Oracle安装目录下搜索\"sqlnet.ora\"文件，进行如下修改：</p>\r\n<p>SQLNET_AUTHENTICATION_SERVICES= (NTS)</p>\r\n<p>改为：</p>\r\n<p>SQLNET_AUTHENTICATION_SERVICES= (NONE)</p>','',1,1,'2011-12-30 13:18:24','2011-12-30 13:18:24',14,'oracle',1),(46,'Python网络编程1 - 套接字','python-network-programming1-socket',1,'<p><strong>1. 套接字</strong></p>\r\n<p>1) 什么是套接字</p>\r\n<p>网络化的应用程序在开始任何通讯之前都必需要创建套接字。就像电话的插口一样，没有它就完全没办法通信。</p>\r\n<p>套接字起源于20世纪70年代加州大学伯克利分校版本的Unix，即人们所说的BSD Unix。因此，有时人们也把套接字称为&ldquo;伯克利套接字&rdquo;或&ldquo;BSD套接字&rdquo;。一开始，套接字被设计用在同一台主机上多个应用程序之间的通讯。这也被称作进程间通讯，或IPC。套接字有两种，分别是基于文件型的和基于网络型的。</p>\r\n<p>Unix套接字是我们要介绍的第一个套接字家族。其&ldquo;家族名&rdquo;为AF_UNIX（在POSIX1.g标准中也叫AF_LOCAL），表示&ldquo;地址家族：UNIX&rdquo;。包括Python在内的大多数流行平台上都使用术语&ldquo;地址家族&rdquo;及其缩写&ldquo;AF&rdquo;。而老一点的系统中，地址家族被称为&ldquo;域&rdquo;或&ldquo;协议家族&rdquo;，并使用缩写&ldquo;PF&rdquo;而不是&ldquo;AF&rdquo;。同样的，AF_LOCAL（在2000-2001年被列为标准）将会代替AF_UNIX。不过，为了向后兼容，很多系统上，两者是等价的。Python自己则仍然使用AF_UNIX。</p>\r\n<p>由于两个进程都运行在同一台机器上，而且这些套接字是基于文件的。所以，它们的底层结构是由文件系统来支持的。这样做相当有道理，因为，同一台电脑上，文件系统的确是不同的进程都能访问的。</p>\r\n<p>另一种套接字是基于网络的，它有自己的家族名字：AF_INET，或叫&ldquo;地址家族：Internet&rdquo;。还有一种地址家族AF_INET6被用于网际协议第6版（IPv6）寻址上。还有一些其他的地址家族，不过，它们要么是只用在某个平台上，要么就是已经被废弃，或是很少被使用，或是根本就还没有实现。所有地址家族中，AF_INET是使用最广泛的一个。Python 2.5中加入了一种Linux套接字的支持：AF_NETLINK（无连接(稍后讲解)）套接字家族让用户代码与内核代码之间的IPC可以使用标准BSD套接字接口。而且，相对之前那些往操作系统中加入新的系统调用、proc文件系统支持或是&ldquo;IOCTL&rdquo;等复杂的方案来说，这种方法显得更为精巧，更为安全。</p>\r\n<p>Python只支持AF_UNIX，AF_NETLINK，和AF_INET家族。由于我们只关心网络编程，所以在本章的大部分时候，我们都只用AF_INET。</p>\r\n<p>2) 套接字地址：主机与端口</p>\r\n<p>如果把套接字比做电话的插口&mdash;即通信的最底层结构，那主机与端口就像区号与电话号码的一对组合。有了能打电话的硬件还不够，你还要知道你要打给谁，往哪打。一个因特网地址由网络通信所必需的主机与端口组成。而且不用说，另一端一定要有人在听才可以。否则，你就会听到熟悉的声音&ldquo;对不起，您拨的是空号，请查询后再拨&rdquo;。你在上网的时候，可能也见过类似的情况，如&ldquo;不能连接该服务器。服务器无响应或不可达&rdquo;。</p>\r\n<p>合法的端口号范围为0～65535。其中，小于1024的端口号为系统保留端口。如果你所使用的是Unix操作系统，那么就可以通过/etc/services文件获得保留的端口号（及其对应的服务/协议和套接字类型）。</p>\r\n<p><strong>2. 面向连接与无连接</strong></p>\r\n<p>1) 面向连接</p>\r\n<p>无论你使用哪一种地址家族，套接字的类型只有两种。一种是面向连接的套接字，即在通信之前一定要建立一条连接，就像跟朋友打电话时那样。这种通信方式也被称为&ldquo;虚电路&rdquo;或&ldquo;流套接字&rdquo;。面向连接的通信方式提供了顺序的、可靠的、不会重复的数据传输，而且也不会被加上数据边界。这也意味着，每一个要发送的信息，可能会被拆分成多份，每一份都会不多不少地正确到达目的地。然后被重新按顺序拼装起来，传给正在等待的应用程序。</p>\r\n<p>实现这种连接的主要协议就是传输控制协议（即TCP）。要创建TCP套接字就得在创建的时候指定套接字类型为SOCK_STREAM。TCP套接字采用SOCK_STREAM这个名字，表达了它作为流套接字的特点。由于这些套接字使用网际协议（IP）来查找网络中的主机，所以这样形成的整个系统，一般会由这两个协议（TCP和IP）名的组合来描述，即TCP/IP。</p>\r\n<p>2) 无连接</p>\r\n<p>与虚电路完全相反的是数据报型的无连接套接字。这意味着，无需建立连接就可以进行通讯。但这时，数据到达的顺序、可靠性及不重复性就无法保证了。数据报会保留数据边界，这就表示，数据是整个发送的，不会像面向连接的协议那样被先拆分成小块。</p>\r\n<p>使用数据报来传输数据就像邮政服务一样。邮件和包裹不一定会按它们发送的顺序到达。事实上，它们还有可能根本到达不了！而且，在网络中报文甚至会重复发送，这也增加了复杂性。</p>\r\n<p>既然数据报有这么多缺点，为什么还要使用它呢？（一定有能胜过流套接字的功能！）由于面向连接套接字要提供一些保证，以及要维持虚电路连接，这都是很重的额外负担。数据报没有这些负担，所以它更&ldquo;便宜&rdquo;。通常能提供更好的性能，更适合某些应用场合。实现这种连接的主要协议就是用户数据报协议（即UDP）。要创建UDP套接字就得在创建的时候指定套接字类型为SOCK_DGRAM。SOCK_DGRAM这个名字，也许你已经猜到了，来自于单词&ldquo;datagram&rdquo;（&ldquo;数据报&rdquo;）。由于这些套接字使用网际协议来查找网络中的主机，这样形成的整个系统，一般会由这两个协议（UDP和IP）名的组合来描述，即UDP/IP。</p>\r\n<p><strong>3. socket()模块函数</strong></p>\r\n<p>现在，你已经有了足够的客户端/服务器架构、套接字和网络方面的知识。我们现在就开始把这些概念带到Python中来。本节中，我们将主要使用socket模块。模块中的socket()函数被用来创建套接字。套接字也有自己的一套函数来提供基于套接字的网络通信。</p>\r\n<p>要使用socket.socket()函数来创建套接字。其语法如下：</p>\r\n<pre class=\"prettyprint\">socket(socket_family, socket_type, protocol=0)</pre>\r\n<p>如前所述，Socket_family不是AF_UNIX就是AF_INET socket_type可以是SOCK_STREAM或SOCK_DGRAM，这一点前面已说过。protocol一般不填，默认值为0。创建一个TCP/IP的套接字，你要这样调用socket.socket()：</p>\r\n<pre class=\"prettyprint\">tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</pre>\r\n<p>同样地，创建一个UDP/IP的套接字，你要这样：</p>\r\n<pre class=\"prettyprint\">udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)</pre>\r\n<p>因为使用了socket模块，所以需要使用\'from socket import *\'，我们就把socket模块里的所有属性都带到我们的命名空间里了，这样能大幅减短我们的代码。</p>\r\n<pre class=\"prettyprint\">tcpSock = socket(AF_INET, SOCK_STREAM)</pre>\r\n<p>当我们创建了套接字对象后，所有的交互都将通过对该套接字对象的方法调用来进行。</p>\r\n<p><strong>4. 套接字对象（内建）方法</strong></p>\r\n<p>下表中我们列出了最常用的套接字对象的方法。在下一个小节中，我们将分别创建TCP和UDP的客户端和服务器，它们都要用到这些方法。虽然我们只关心因特网套接字，但是这些方法在Unix套接字中的也有类似的意义。</p>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<th colspan=\"2\">服务器端套接字函数</th>\r\n</tr>\r\n<tr>\r\n<td>s.bind()</td>\r\n<td>绑定地址（主机名，端口号对）到套接字</td>\r\n</tr>\r\n<tr>\r\n<td>s.listen()</td>\r\n<td>开始TCP监听</td>\r\n</tr>\r\n<tr>\r\n<td>s.accept()</td>\r\n<td>被动接受TCP客户端连接，（阻塞式）等待连接的到来</td>\r\n</tr>\r\n<tr>\r\n<th colspan=\"2\">客户端套接字函数</th>\r\n</tr>\r\n<tr>\r\n<td>s.connect()</td>\r\n<td>主动初始化TCP服务器连接</td>\r\n</tr>\r\n<tr>\r\n<td>s.connect_ex()</td>\r\n<td>connect()函数的扩展版本，出错时返回出错码，而不是抛出异常</td>\r\n</tr>\r\n<tr>\r\n<th colspan=\"2\">公共用途的套接字函数</th>\r\n</tr>\r\n<tr>\r\n<td>s.recv()</td>\r\n<td>接收TCP数据</td>\r\n</tr>\r\n<tr>\r\n<td>s.send()</td>\r\n<td>发送TCP数据</td>\r\n</tr>\r\n<tr>\r\n<td>s.sendall()</td>\r\n<td>完整发送TCP数据</td>\r\n</tr>\r\n<tr>\r\n<td>s.recvfrom()</td>\r\n<td>接收UDP数据</td>\r\n</tr>\r\n<tr>\r\n<td>s.sendto()</td>\r\n<td>发送UDP数据</td>\r\n</tr>\r\n<tr>\r\n<td>s.getpeername()</td>\r\n<td>连接到当前套接字的远端的地址（TCP连接）</td>\r\n</tr>\r\n<tr>\r\n<td>s.getsockname()</td>\r\n<td>当前套接字的地址</td>\r\n</tr>\r\n<tr>\r\n<td>s.getsockopt()</td>\r\n<td>返回指定套接字的参数</td>\r\n</tr>\r\n<tr>\r\n<td>s.setsockopt()</td>\r\n<td>设置指定套接字的参数</td>\r\n</tr>\r\n<tr>\r\n<td>s.close()</td>\r\n<td>关闭套接字</td>\r\n</tr>\r\n<tr>\r\n<th colspan=\"2\">公共用途的套接字函数</th>\r\n</tr>\r\n<tr>\r\n<td>s.setblocking()</td>\r\n<td>设置套接字的阻塞与非阻塞模式</td>\r\n</tr>\r\n<tr>\r\n<td>s.settimeout()</td>\r\n<td>设置阻塞套接字操作的超时时间</td>\r\n</tr>\r\n<tr>\r\n<td>s.gettimeout()</td>\r\n<td>得到阻塞套接字操作的超时时间</td>\r\n</tr>\r\n<tr>\r\n<th colspan=\"2\">公共用途的套接字函数</th>\r\n</tr>\r\n<tr>\r\n<td>s.fileno()</td>\r\n<td>套接字的文件描述符</td>\r\n</tr>\r\n<tr>\r\n<td>s.makefile()</td>\r\n<td>创建一个与该套接字关连的文件对象</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p><strong>5. Socket模块属性</strong></p>\r\n<p>除了以上函数外，socket模块还有很多属性可供网络应用程序使用。下表中列出了最常用的几个。</p>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<th colspan=\"2\">数据属性</th>\r\n</tr>\r\n<tr>\r\n<td>AF_UNIX, AF_INET, AF_INET6</td>\r\n<td>Python支持的套接字地址家族</td>\r\n</tr>\r\n<tr>\r\n<td>SOCK_STREAM, SOCK_DGRAM</td>\r\n<td>套接字类型（TCP = 流，UDP = 数据报）</td>\r\n</tr>\r\n<tr>\r\n<td>has_ipv6</td>\r\n<td>表示是否支持IPv6的布尔型标志</td>\r\n</tr>\r\n<tr>\r\n<th colspan=\"2\">异常</th>\r\n</tr>\r\n<tr>\r\n<td>error</td>\r\n<td>套接字相关错误</td>\r\n</tr>\r\n<tr>\r\n<td>herror</td>\r\n<td>主机和地址相关的错误</td>\r\n</tr>\r\n<tr>\r\n<td>gaierror</td>\r\n<td>地址相关的错误</td>\r\n</tr>\r\n<tr>\r\n<td>timeout</td>\r\n<td>超时</td>\r\n</tr>\r\n<tr>\r\n<th colspan=\"2\">函数</th>\r\n</tr>\r\n<tr>\r\n<td>socket()</td>\r\n<td>用指定的地址家族、套接字类型和协议类型创建一个套接字对象（可选）</td>\r\n</tr>\r\n<tr>\r\n<td>socketpair()</td>\r\n<td>用指定的地址家族、套接字类型和协议类型创建一对套接字对象（可选）</td>\r\n</tr>\r\n<tr>\r\n<td>fromfd()</td>\r\n<td>用一个已经打开的文件描述符创建一个套接字对象</td>\r\n</tr>\r\n<tr>\r\n<td>ssl()</td>\r\n<td>在套接字上发起一个安全套接字层（SSL）。不做证书验证</td>\r\n</tr>\r\n<tr>\r\n<td>getaddrinfo()</td>\r\n<td>得到地址信息</td>\r\n</tr>\r\n<tr>\r\n<td>getfqdn()</td>\r\n<td>返回完整的域的名字</td>\r\n</tr>\r\n<tr>\r\n<td>getfqdn()</td>\r\n<td>返回完整的域的名字</td>\r\n</tr>\r\n<tr>\r\n<td>gethostname()</td>\r\n<td>得到当前主机名</td>\r\n</tr>\r\n<tr>\r\n<td>gethostbyname()</td>\r\n<td>由主机名得到对应的IP地址</td>\r\n</tr>\r\n<tr>\r\n<td>gethostbyname_ex()</td>\r\n<td>gethostbyname()的扩展版本，返回主机名、主机所有的别名和IP地址列表</td>\r\n</tr>\r\n<tr>\r\n<td>gethostbyaddr()</td>\r\n<td>由IP地址得到DNS信息，返回一个类似gethostbyname_ex()的3元组</td>\r\n</tr>\r\n<tr>\r\n<td>getprotobyname()</td>\r\n<td>由协议名（如\'tcp\'）得到对应的号码</td>\r\n</tr>\r\n<tr>\r\n<td>getservbyname()/getservbyport()</td>\r\n<td>由服务名得到对应的端口号或反之；两个函数中，协议名都是可选的</td>\r\n</tr>\r\n<tr>\r\n<td>ntohl()/ntohs()</td>\r\n<td>把一个整型由网络字节序转换为主机字节序</td>\r\n</tr>\r\n<tr>\r\n<td>htonl()/htons()</td>\r\n<td>把一个整型由主机字节序转换为网络字节序</td>\r\n</tr>\r\n<tr>\r\n<td>inet_aton()/inet_ntoa()</td>\r\n<td>把IP地址转为32位整型，或反之（仅对IPv4地址有效）</td>\r\n</tr>\r\n<tr>\r\n<td>inet_pton()/inet_ntop()</td>\r\n<td>把IP地址转为二进制格式或反之（对IPv4和Ipv6地址都有效）</td>\r\n</tr>\r\n<tr>\r\n<td>getdefaulttimeout()/setdefaulttimeout()</td>\r\n<td>得到/设置默认的套接字超时时间，单位秒（浮点型）</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>请参考《Python Library Reference》中socket模块的文档以了解更多的信息。</p>','',1,1,'2012-01-01 01:27:25','2012-01-01 01:27:25',2,'python, network',NULL),(47,'Python网络编程2 - TCP服务器','python-network-programming2-tcpserver',1,'<p><strong>1. 创建一个TCP服务器</strong></p>\r\n<p>我们首先将给出一个关于如何创建一个通用的TCP服务器的伪代码，然后解释会发生什么问题。要注意的是，这只是设计服务器的一种方法，当你对服务器的设计有了一定的了解之后，你就能用你所希望的方式来修改这段伪代码：</p>\r\n<pre class=\"prettyprint\">ss = socket()       # 创建服务器套接字\r\nss.bind()           # 把地址绑定到套接字上\r\nss.listen()         # 监听连接\r\ninf_loop:           # 服务器无限循环\r\ncs = ss.accept()    # 接受客户端连接\r\ncomm_loop:          # 通信循环\r\ncs.recv()/cs.send() # 对话（接收与发送）\r\ncs.close()          # 关闭客户端套接字\r\nss.close()          # 关闭服务器套接字（可选）</pre>\r\n<p>所有的套接字都用socket.socket()函数来创建。服务器需要&ldquo;坐在某个端口上&rdquo;等待请求。所以它们必需要&ldquo;绑定&rdquo;到一个本地的地址上。由于TCP是一个面向连接的通信系统，在TCP服务器可以开始工作之前，要先完成一些设置。TCP服务器必须&ldquo;监听&rdquo;（进来的）连接，设置完成之后，服务器就可以进入无限循环了。</p>\r\n<p>一个简单的（单线程的）服务器会调用accept()函数等待连接的到来。默认情况下，accept()函数是阻塞式的，即程序在连接到来之前会处于挂起状态。套接字也支持非阻塞模式。请参阅相关文档或操作系统手册以了解为何及如何使用非阻塞套接字。</p>\r\n<p>一旦接收到一个连接，accept()函数就会返回一个单独的客户端套接字用于后续的通信。使用新的客户端套接字就像把客户的电话转给一个客户服务人员。当一个客户打电话进来的时候，总机接了电话，然后把电话转到合适的人那里来处理客户的需求。</p>\r\n<p>这样就可以空出总机，也就是最初的那个服务器套接字，于是，话务员就可以等待下一个电话（客户端请求），与此同时，前一个客户与对应的客户服务人员在另一条线路上进行着他们之间的对话。同样的，当一个请求到来时，要创建一个新的端口，然后直接在那个端口上与客户对话，这样就可以空出主端口来接受其他客户的连接。</p>\r\n<p>核心提示：创建线程来处理客户端请求。</p>\r\n<p>我们不打算在例子里实现这样的功能。但是，创建一个新的线程或进程来完成与客户端通讯是一种非常常用的手段。SocketServer模块是一个基于socket模块的高级别的套接字通讯模块，它支持在新的线程或进程中处理客户端请求。</p>\r\n<p>在临时套接字创建好之后，通信就可以开始了。客户与服务器都使用这个新创建的套接字进行数据的发送与接收，直到通讯的某一方关闭了连接或发送了一个空字符串之后，通讯就结束了。</p>\r\n<p>在代码中，当客户端连接关闭后，服务器继续等待下一个客户端的连接。代码的最后一行，会把服务器的套接字关闭。由于服务器处在无限循环中，不可能会走到这一步，所以，这一步是可选的。我们写这一句话的主要目的是要提醒读者，在设计一个更智能的退出方案时，比方说，服务器被通知要关闭时，要确保close()函数会被调用。</p>\r\n<p>在tsTserv.py文件中，会创建一个TCP服务器程序，这个程序会把客户端发送过来的字符串加上一个时间戳（格式：\'[时间]数据\'）返回给客户端（&ldquo;tsTserv&rdquo;代表时间戳TCP服务器。其他文档的命令将与此类似）。</p>\r\n<p>创建一个能接收客户端的消息，在消息前加一个时间戳后返回的TCP服务器</p>\r\n<pre class=\"prettyprint\">#!/usr/bin/env python\r\n\r\nfrom socket import *\r\nfrom time import ctime\r\n\r\nHOST=\'\'\r\nPORT=21567\r\nBUFSIZ = 1024\r\nADDR = (HOST, PORT)\r\n\r\ntcpSerSock = socket(AF_INET, SOCK_STREAM)\r\ntcpSerSock.bind(ADDR)\r\ntcpSerSock.listen(5)\r\n\r\nwhile True:\r\n    print \'waiting for connection...\'\r\n    tcpCliSock, addr = tcpSerSock.accept()\r\n    print \'...connected from:\', addr\r\n\r\n    while True:\r\n         data = tcpCliSock.recv(BUFSIZ)\r\n         if not data:\r\n             break\r\n         tcpCliSock.send(\'[%s] %s\' % (ctime(), data))\r\n\r\n    tcpCliSock.close()\r\ntcpSerSock.close()</pre>\r\n<p>逐行解释</p>\r\n<p>1～4行</p>\r\n<p>第1行是Unix的启动信息行，随后我们导入了time.ctime()函数和socket模块的所有属性。</p>\r\n<p>6～13行</p>\r\n<p>HOST变量为空，表示bind()函数可以绑定在所有有效的地址上。我们还选用了一个随机生成的未被占用的端口号。在程序中，我们把缓冲区的大小设定为1K。你可以根据网络情况和应用的需要来修改这个大小。listen()函数的参数只是表示最多允许多少个连接同时连进来，而后来的连接就会被拒绝掉。</p>\r\n<p>TCP服务器的套接字（tcpSerSock）在第11行被生成。随后把套接字绑定到服务器的地址上，然后开始TCP监听。</p>\r\n<p>15～28行</p>\r\n<p>在进入到服务器的无限循环后，我们（被动地）等待连接的到来。当有连接时，我们进入对话循环，等待客户端发送数据。如果消息为空，表示客户端已经退出，那就再去等待下一个客户端连接。得到客户端消息后，我们在消息前加一个时间戳然后返回。最后一行不会被执行到，放在这里用于提醒读者，在服务器要退出的时候，要记得调用close()函数。</p>\r\n<p><strong>2. 创建TCP客户端</strong></p>\r\n<p>创建TCP客户端相对服务器来说更为容易。与TCP服务器那节类似，我们也是先给出伪代码及其解释，然后再给出真正的代码。</p>\r\n<pre class=\"prettyprint\">cs = socket()       # 创建客户端套接字\r\ncs.connect()        # 尝试连接服务器\r\ncomm_loop:          # 通信循环\r\ncs.send()/cs.recv() # 对话（发送/接收）\r\ncs.close()          # 关闭客户端套接字</pre>\r\n<p>如前所述，所有的套接字都由socket.socket()函数创建。在客户端有了套接字之后，马上就可以调用connect()函数去连接服务器。连接建立后，就可以与服务器开始对话了。在对话结束后，客户端就可以关闭套接字，结束连接。</p>\r\n<p>我们给出了TsTclnt.py的代码。程序连接到服务器，提示用户输入要传输的数据，然后通过客户端代码显示服务器返回的加了时间戳的结果。</p>\r\n<p>逐行解释</p>\r\n<p>1～3行</p>\r\n<p>第1行是Unix的启动信息行，随后我们导入了socket模块的所有属性。</p>\r\n<p>例 TCP时间戳客户端（tsTclnt.py）创建一个TCP客户端，程序会提示用户输入要传给服务器的信息，显示服务器返回的加了时间戳的结果。</p>\r\n<pre class=\"prettyprint\">#!/usr/bin/env python\r\n\r\nfrom socket import *\r\n\r\nHOST=\' localhost \'\r\nPORT=21567\r\nBUFSIZ = 1024\r\nADDR = (HOST, PORT)\r\n\r\ntcpCliSock = socket(AF_INET, SOCK_STREAM)\r\ntcpCliSock.connect(ADDR)\r\n\r\nwhile True:\r\n    data = raw_input(\'&gt; \')\r\n    if not data:\r\n        break\r\n    tcpCliSock.send(data)\r\n    data = tcpCliSock.recv(BUFSIZ)\r\n    if not data:\r\n        break\r\n    print data\r\n\r\ntcpCliSock.close()</pre>\r\n<p>5～11行</p>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 1721px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">HOST和PORT变量表示服务器的主机名与端口号。由于我们在同一台电脑上进行测试，所以HOST里放的是本机的主机名（如果你的服务器运行在其他电脑上，要做相应的修改）。端口号要与服务器上的设置完全相同（不然就没办法通信了）。缓冲区的大小还是设为1K。</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 1721px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">TCP客户套接字（tcpCliSock）在第10行创建。然后就去连接服务器。</div>\r\n<p>HOST和PORT变量表示服务器的主机名与端口号。由于我们在同一台电脑上进行测试，所以HOST里放的是本机的主机名（如果你的服务器运行在其他电脑上，要做相应的修改）。端口号要与服务器上的设置完全相同（不然就没办法通信了）。缓冲区的大小还是设为1K。TCP客户套接字（tcpCliSock）在第10行创建。然后就去连接服务器。</p>\r\n<p>13～23行</p>\r\n<p>客户端也有一个无限循环，但这跟服务器的那个不期望退出的无限循环不一样。客户端的循环在以下两个条件的任意一个发生后就退出：用户没有输入任何内容（14～16行）或服务器由于某种原因退出，导致recv()函数失败（18～20行）。否则，在一般情况下，客户端会把用户输入的字符串发给服务器进行处理，然后接收并显示服务器传回来的加了时间戳的字符串。</p>\r\n<p><strong>3.&nbsp;运行我们的客户端与TCP服务器</strong></p>\r\n<p>现在，我们来运行服务器和客户端程序，看看它们的运行情况如何。我们应该先运行服务器还是客户呢？很显然，如果我们先运行客户端，由于没有服务器在等待请求，客户端没办法做连接。服务器是一个被动端，它先创建自己然后被动地等待连接。而客户端则是主动端，由它主动地建立一个连接。所以：要先开服务器，后开客户端。</p>\r\n<p>我们在运行客户端和服务器的例子中，使用了同一台电脑。其实也可以把服务器放在其他的电脑上，这时只要改改主机名就好了。</p>\r\n<p>下面就是客户端的输入与输出，不输入数据直接按回车键就可以退出程序：</p>\r\n<pre class=\"prettyprint\">$ tsTclnt.py\r\n&gt; hi\r\n[Sat Jun 17 17:27:21 2006] hi\r\n&gt; spanish inquisition\r\n[Sat Jun 17 17:27:37 2006] spanish inquisition\r\n&gt;\r\n$</pre>\r\n<p>服务器的输出主要用于调试目的：</p>\r\n<pre class=\"prettyprint\">$ tsTserv.py\r\nwaiting for connection...\r\n...connected from: (\'127.0.0.1\', 1040)\r\nwaiting for connection...</pre>\r\n<p>当有客户端连接上来的时候，会显示一个&ldquo;... connected from ...&rdquo;信息。在客户端接受服务的时候，服务器又回去等待其他客户端连接。在从服务器退出的时候，我们要跳出那个无限循环，这时会触发一个异常。避免这种错误的方法是采用一种更优雅的退出方式。</p>\r\n<p>&ldquo;友好地&rdquo;退出的一个方法就是把服务器的while循环放在一个try-except语句的except子句当中，并捕获EOFError和KeyboardInterrupt异常。在except子句中，调用close()函数关闭服务器的套接字。</p>','',1,1,'2012-01-01 02:35:52','2012-01-01 02:35:52',2,'python, network',NULL),(48,'Python网络编程3 - UDP服务器','python-network-programming3-udpserver',1,'<p><strong>1. 创建一个UDP服务器</strong></p>\r\n<p>由于UDP服务器不是面向连接的，所以不用像TCP服务器那样做那么多设置工作。事实上，并不用设置什么东西，直接等待进来的连接就好了。</p>\r\n<pre class=\"prettyprint\">ss = socket()                  # 创建一个服务器套接字\r\nss.bind()                      # 绑定服务器套接字\r\ninf_loop:                      # 服务器无限循环\r\ncs = ss.recvfrom()/ss.sendto() # 对话（接收与发送）\r\nss.close()                     # 关闭服务器套接字</pre>\r\n<p>从伪代码中可以看出，使用的还是那套先创建套接字然后绑定到本地地址（主机/端口对）的方法。无限循环中包含了从客户端接收消息，返回加了时间戳的结果和回去等下一个消息这三步。同样的，由于代码不会跳出无限循环，所以，close()函数调用是可选的。我们写这一句话的原因是要提醒读者，在设计一个更智能的退出方案的时候，要确保close()函数会被调用。</p>\r\n<p>UDP时间戳服务器（tsUserv.py）创建一个能接收客户端消息、在消息前加一个时间戳后返回的UDP服务器。</p>\r\n<pre class=\"prettyprint\">#!/usr/bin/env python\r\n\r\nfrom socket import *\r\nfrom time import ctime\r\n\r\nHOST=\'\'\r\nPORT=21567\r\nBUFSIZ = 1024\r\nADDR = (HOST, PORT)\r\n\r\nudpSerSock = socket(AF_INET, SOCK_DGRAM)\r\nudpSerSock.bind(ADDR)\r\n\r\nwhile True:\r\n    print \'waiting for message...\'\r\n    data, addr = udpSerSock.recvfrom(BUFSIZ)\r\n    udpSerSock.sendto(\'[%s] %s\' % (ctime(), data), addr)\r\n    print \'...received from and returned to:\', addr\r\n\r\nudpSerSock.close()</pre>\r\n<p>UDP和TCP服务器的另一个重要的区别是，由于数据报套接字是无连接的，所以无法把客户端连接交给另外的套接字进行后续的通讯。这些服务器只是接受消息，需要的话，给客户端返回一个结果就可以了。</p>\r\n<p>上面的tsUserv.py是之前那个TCP服务器的UDP版本，它接收客户端消息，加时间戳后返回给客户端。</p>\r\n<p>逐行解释</p>\r\n<p>1～4行</p>\r\n<p>就像TCP服务器的设置那样，在Unix的启动信息行后，我们导入了time.ctime()函数和socket模块的所有属性。</p>\r\n<p>6～12行</p>\r\n<p>HOST和PORT变量与TCP服务器的完全一样。socket()函数的调用有一些不同，我们现在要的是一个数据报/UDP的套接字类型。不过bind()函数的调用方式还是跟TCP版本的一样。同样地，由于UDP是无连接的，就不用调用listen()函数来监听进来的连接了。</p>\r\n<p>14～21行</p>\r\n<p>在进入到服务器的无限循环后，我们（被动地）等待（数据报）消息的到来。当有消息进来时，就处理它（在前面加时间戳），把结果返回去，然后再去等待下一个消息。就像之前一样，那个close()函数只是一个演示而已。</p>\r\n<p><strong>2. 创建一个UDP客户端</strong></p>\r\n<p>这一节中介绍的4段程序中，下面的这段UDP客户端代码是最短的。伪代码如下：</p>\r\n<pre class=\"prettyprint\">cs = socket()             # 创建客户端套接字\r\ncomm_loop:                # 通讯循环\r\ncs.sendto()/cs.recvfrom() # 对话（发送/接收）\r\ncs.close()                # 关闭客户端套接字</pre>\r\n<p>在套接字对象创建好之后，我们就进入一个与服务器的对话循环。在通信结束后，套接字就被关闭了。tsUclnt.py真实的代码在下面的代码清单中中给出。</p>\r\n<pre class=\"prettyprint\">#!/usr/bin/env python\r\n\r\nfrom socket import *\r\n\r\nHOST=\' localhost \'\r\nPORT=21567\r\nBUFSIZ = 1024\r\nADDR = (HOST, PORT)\r\n\r\nudpCliSock = socket(AF_INET, SOCK_DGRAM)\r\n\r\nwhile True:\r\n    data = raw_input(\'&gt; \')\r\n    if not data:\r\n        break\r\n    udpCliSock.sendto(data, ADDR)\r\n    data, ADDR = udpCliSock.recvfrom(BUFSIZ)\r\n    if not data:\r\n        break\r\n    print dataudpClisock.close()\r\n\r\nudpCliSock.close()</pre>\r\n<p>逐行解释</p>\r\n<p>1～3行</p>\r\n<p>还是跟TCP版本的客户端一样，在Unix的启动信息行后，我们导入了socket模块的所有属性。</p>\r\n<p>5～10行</p>\r\n<p>因为我们的服务器也是运行在本机，我们的客户端还是使用本机和相同的端口号。自然地，缓冲区的大小也还是1K。创建套接字的方法跟UDP服务器中的一样。</p>\r\n<p>12～22行</p>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 1168px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">UDP客户端的循环基本上与TCP客户端的完全一样。唯一的区别就是，我们不用先去跟UDP服务器建立连接，而是直接把消息发送出去，然后等待服务器的回复。得到加了时间戳的字符串后，把它显示到屏幕上，然后再继续其他的消息。在输入结束后，退出循环，关闭套接字。</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 1168px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">例16.4 &nbsp;UDP时间戳客户端（tsUclnt.py）</div>\r\n<p>UDP客户端的循环基本上与TCP客户端的完全一样。唯一的区别就是，我们不用先去跟UDP服务器建立连接，而是直接把消息发送出去，然后等待服务器的回复。得到加了时间戳的字符串后，把它显示到屏幕上，然后再继续其他的消息。在输入结束后，退出循环，关闭套接字。例16.4 &nbsp;UDP时间戳客户端（tsUclnt.py）</p>\r\n<p>创建一个UDP客户端，程序会提示用户输入要传给服务器的信息，显示服务器返回的加了时间戳的结果。</p>\r\n<p><strong>3.&nbsp;执行UDP服务器和客户端</strong></p>\r\n<p>UDP客户端与TCP客户端的表现类似：</p>\r\n<pre class=\"prettyprint\">$ tsUclnt.py\r\n&gt; hi\r\n[Sat Jun 17 19:55:36 2006] hi\r\n&gt; spam! spam! spam!\r\n[Sat Jun 17 19:55:40 2006] spam! spam! spam!\r\n&gt;\r\n$</pre>\r\n<p>服务器也差不多：</p>\r\n<pre class=\"prettyprint\">$ tsUserv.py\r\nwaiting for message...\r\n...received from and returned to: (\'127.0.0.1\', 1025)\r\nwaiting for message...</pre>\r\n<p>我们输出客户端信息的原因是，服务器可能会得到并回复多个客户端消息，这时，输出就可以让我们了解消息来自哪里。对于TCP服务器来说，由于客户端会创建一个连接，我们自然就能知道消息来自哪里。注意，我们的提示信息写的是&ldquo;waiting for message&rdquo;（&ldquo;等待消息&rdquo;）而不是&ldquo;waiting for connection&rdquo;（&ldquo;等待连接&rdquo;）。</p>','',1,1,'2012-01-01 20:51:24','2012-01-01 20:51:24',2,'python, network',NULL),(49,'Oracle 表空间与数据文件','oracle-tablespace-and-datafile',1,'<p><strong>一： 概念</strong></p>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td>表空间</td>\r\n<td>是一个或多个数据文件的逻辑集合</td>\r\n</tr>\r\n<tr>\r\n<td valign=\"top\">表空间逻辑存储对象</td>\r\n<td>永久段--&gt;如表与索引<br />临时段--&gt;如临时表数据与排序段<br />回滚段--&gt;用于事物回滚或闪回内存的撤销数据</td>\r\n</tr>\r\n<tr>\r\n<td>表空间分类</td>\r\n<td>系统表空间(system、sysaux)，非系统表空间</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>一个表空间至少包含一个数据文件，一个数据文件只能属于一个表空间。</p>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<th colspan=\"2\" align=\"left\">不可或缺的几个表空间：</th>\r\n</tr>\r\n<tr>\r\n<td>SYSTEM</td>\r\n<td>字典表空间，不能被损坏</td>\r\n</tr>\r\n<tr>\r\n<td>UNDO</td>\r\n<td>dml,dql把数据快照到此，数据提交即消失（用于恢复）</td>\r\n</tr>\r\n<tr>\r\n<td>SYSAUX</td>\r\n<td>10g 高并发系统繁忙时，会造成system争用，将工具放到SYSAUX,减轻system的压力，SYSAUX不影响系统（影响性能）</td>\r\n</tr>\r\n<tr>\r\n<td>TEMP</td>\r\n<td>临时数据相关的内容</td>\r\n</tr>\r\n<tr>\r\n<td>USERS</td>\r\n<td>10g  用户数据从system拨离出来</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p><strong>二： Oracle的存储结构</strong></p>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td>1.Schema：</td>\r\n<td>用户---&gt;创建相关对象、表、视图、序列、函数、存储过程、包等的集合</td>\r\n</tr>\r\n<tr>\r\n<td>2.逻辑结构：</td>\r\n<td valign=\"top\">database数据库---&gt;tablespace表空间---&gt; segment段---&gt;extent区间----&gt; block块<br />逻辑结构是Oracle内部管理数据库中对象的方式</td>\r\n</tr>\r\n<tr>\r\n<td>3.物理结构：</td>\r\n<td>OS  block ---&gt;datafile 物理结构通常是一系列数据文件</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>举例描述用户创建对象的组织方式</p>\r\n<p>查看用户的默认表空间及数据文件</p>\r\n<pre class=\"prettyprint\">column USERNAME FORMAT A25\r\ncolumn DEFAULT_TABLESPACE FORMAT A20\r\ncolumn TEMPORARY_TABLESPACE FORMAT A21\r\n\r\nSELECT USERNAME\r\n     , DEFAULT_TABLESPACE\r\n     , TEMPORARY_TABLESPACE\r\nFROM\r\n    dba_users\r\nORDER BY\r\n    username ASC;</pre>\r\n<p>结果（一个新创建的数据库大概会有30个用户）：</p>\r\n<pre class=\"prettyprint\">USERNAME                  DEFAULT_TABLESPACE   TEMPORARY_TABLESPACE\r\n------------------------- -------------------- ---------------------\r\nANONYMOUS                 SYSAUX               TEMP\r\nAPEX_PUBLIC_USER          USERS                TEMP\r\nCTXSYS                    SYSAUX               TEMP\r\nDBSNMP                    SYSAUX               TEMP\r\nDIP                       USERS                TEMP\r\nEXFSYS                    SYSAUX               TEMP\r\nFLOWS_030000              SYSAUX               TEMP\r\nFLOWS_FILES               SYSAUX               TEMP\r\nMDDATA                    USERS                TEMP\r\nMDSYS                     SYSAUX               TEMP\r\nMGMT_VIEW                 SYSTEM               TEMP\r\nOLAPSYS                   SYSAUX               TEMP\r\nORACLE_OCM                USERS                TEMP\r\nORDPLUGINS                SYSAUX               TEMP\r\nORDSYS                    SYSAUX               TEMP\r\nOUTLN                     SYSTEM               TEMP\r\nOWBSYS                    SYSAUX               TEMP\r\nSI_INFORMTN_SCHEMA        SYSAUX               TEMP\r\nSPATIAL_CSW_ADMIN_USR     USERS                TEMP\r\nSPATIAL_WFS_ADMIN_USR     USERS                TEMP\r\nSYS                       SYSTEM               TEMP\r\nSYSMAN                    SYSAUX               TEMP\r\nSYSTEM                    SYSTEM               TEMP\r\nTSMSYS                    USERS                TEMP\r\nWKPROXY                   SYSAUX               TEMP\r\nWKSYS                     SYSAUX               TEMP\r\nWK_TEST                   SYSAUX               TEMP\r\nWMSYS                     SYSAUX               TEMP\r\nXDB                       SYSAUX               TEMP\r\nXS$NULL                   USERS                TEMP</pre>\r\n<p>查看表空间及数据文件</p>\r\n<pre class=\"prettyprint\">column TS_NAME FORMAT A15\r\ncolumn DF_NAME FORMAT A50\r\n\r\nSELECT t1.NAME AS TS_NAME\r\n     , t2.NAME AS DF_NAME\r\nFROM\r\n    v$tablespace t1, v$datafile t2 \r\nWHERE\r\n    t1.ts# = t2.ts#;</pre>\r\n<p>结果如下：</p>\r\n<pre class=\"prettyprint\">TS_NAME         DF_NAME\r\n--------------- ---------------------------------------\r\nSYSTEM          F:\\ORACLEDB\\ORADATA\\MYDB\\SYSTEM01.DBF\r\nSYSAUX          F:\\ORACLEDB\\ORADATA\\MYDB\\SYSAUX01.DBF\r\nUNDOTBS1        F:\\ORACLEDB\\ORADATA\\MYDB\\UNDOTBS01.DBF\r\nUSERS           F:\\ORACLEDB\\ORADATA\\MYDB\\USERS01.DBF</pre>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td style=\"width: 60px;\">表(table)</td>\r\n<td style=\"width: 120px;\">--&gt;数据定义(位于)</td>\r\n<td>--&gt;system表空间</td>\r\n<td>&nbsp;</td>\r\n<td>&nbsp;</td>\r\n<td>&nbsp;</td>\r\n</tr>\r\n<tr>\r\n<td>&nbsp;</td>\r\n<td>--&gt;数据(位于)</td>\r\n<td style=\"width: 160px;\">--&gt;user表空间(逻辑存储)</td>\r\n<td>--&gt;表段</td>\r\n<td>--&gt;区间</td>\r\n<td>--&gt;内存块</td>\r\n</tr>\r\n<tr>\r\n<td>&nbsp;</td>\r\n<td>&nbsp;</td>\r\n<td>&nbsp;</td>\r\n<td>--&gt;索引段等</td>\r\n<td>--&gt;区间</td>\r\n<td>--&gt;内存块</td>\r\n</tr>\r\n<tr>\r\n<td>&nbsp;</td>\r\n<td>&nbsp;</td>\r\n<td>--&gt;user表空间(物理存储)</td>\r\n<td>--&gt;user01.dbf</td>\r\n<td>&nbsp;</td>\r\n<td>&nbsp;</td>\r\n</tr>\r\n<tr>\r\n<td>&nbsp;</td>\r\n<td>&nbsp;</td>\r\n<td>&nbsp;</td>\r\n<td colspan=\"3\">--&gt;采用本地管理，包含头部信息，可用、已用等位图信息</td>\r\n</tr>\r\n<tr>\r\n<td colspan=\"6\">当databuffer缓冲区满则调用dbwr进程将数据写入到物理文件当中</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p><strong>三： 创建表空间</strong></p>\r\n<p>简要语法：</p>\r\n<p>&nbsp; &nbsp; CREATE [BIGFILE | SMALLFILE] [TEMPORARY] TABLESPACE tablespace name<br />&nbsp; &nbsp; DATAFILE datafile spec | TEMPFILE tempfile spec<br />&nbsp; &nbsp; [MINIMUM EXTENT minimum extent size]<br />&nbsp; &nbsp; [BLOCKSIZE blocksize]<br />&nbsp; &nbsp; [[COMPRESS|NOCOMPRESS] DEFAULT STORAGE (default storage clause)]<br />&nbsp; &nbsp; [LOGGING|NOLOGGING]<br />&nbsp; &nbsp; [FORCE LOGGING]<br />&nbsp; &nbsp; [ONLINE|OFFLINE]<br />&nbsp; &nbsp; [EXTENT MANAGEMENT DICTIONARY |<br />&nbsp; &nbsp; LOCAL [AUTOALLOCATE|UNIFORM SIZE size]]<br />&nbsp; &nbsp; [SEGMENT SPACE MANAGEMENT MANUAL|AUTO]<br />&nbsp; &nbsp; [FLASHBACK ON|OFF]&nbsp;</p>\r\n<p>创建表空间的条件</p>\r\n<p>1.具有create tablespace的权限，DBA角色具有该权限，sysdba,sysoper<br />2.创建的是bigfiel ，还是smallifle &nbsp;超过T 级别应考虑bigfile<br />3.新建的表空间的I/O，是否会导致磁盘I/O不够用<br />4.datafile后跟的路径应该具备写的权限&nbsp;</p>\r\n<p>查看缺省是BIGFILE 还是SMALLFILE,当缺省为SMALLFILE,创建表空间不指定表空间类型则为SMALLFILE</p>\r\n<pre class=\"prettyprint\">col PROPERTY_NAME format a20\r\ncol PROPERTY_VALUE format a20\r\n\r\nSELECT PROPERTY_NAME\r\n     , PROPERTY_VALUE\r\nFROM\r\n    database_properties\r\nWHERE\r\n    PROPERTY_NAME LIKE \'%TBS%\';</pre>\r\n<pre class=\"prettyprint\">PROPERTY_NAME        PROPERTY_VALUE\r\n-------------------- --------------------\r\nDEFAULT_TBS_TYPE     SMALLFILE</pre>\r\n<p>修改创建表空间为大或小表空间的默认值</p>\r\n<pre class=\"prettyprint\">alter database set default bigfile tablespace;</pre>\r\n<p>演示创建表空间</p>\r\n<pre class=\"prettyprint\">create tablespace TBS1 \r\n    datafile \'F:\\ORACLEDB\\ORADATA\\MYDB\\tbs1.dbf\' size 100m;</pre>\r\n<p><strong>四： 表空间的管理方式</strong></p>\r\n<p>1. 字典管理（DMT）： oracle 8i(不包括i），只存在一种表空间的管理模式，即字典管理表空间（DMT）</p>\r\n<p>DMT是指oracle的空间分配或回收是通过数据库中的数据字典表来记录和管理的，用于管理的两个数据字典表分别是：UET$（used extents)和FET$(freeextents)，其工作方式是：当建立一个新的段或者段在表空间时，oracle通过一系列的SQL语句来完成这个工作且和前面的两个字典表有关，在繁忙的系统中会造成竞争和等待（另一个DMT会带来的问题是空间碎片）</p>\r\n<p>2. 本地管理（LMT）： 在i的R2版本后成了默认的选项</p>\r\n<p>LMT在表空间的数据文件头部加入了一个位图区域，在其中记录每个extent的使用状况，当extent被使用或者被释放，oracle会更新头部的记录来反映这个变化，不产生回滚信息，因为仅仅操作数据文件头部的几个数据块，不用操作数据字典，LMT比DMT要快，尤其是在繁忙的时候更明显</p>\r\n<p>查看表空间使用的管理方式：</p>\r\n<pre class=\"prettyprint\">col TABLESPACE_NAME format a15\r\ncol EXTENT_MANAGEMENT format a17\r\ncol STATUS format a7\r\ncol CONTENTS format a10\r\n\r\nSELECT TABLESPACE_NAME\r\n     , EXTENT_MANAGEMENT\r\n     , BLOCK_SIZE\r\n     , STATUS\r\n     , CONTENTS\r\n     , FORCE_LOGGING\r\n     , BIGFILE\r\nFROM\r\n    dba_tablespaces;</pre>\r\n<pre class=\"prettyprint\">TABLESPACE_NAME EXTENT_MANAGEMENT BLOCK_SIZE STATUS  CONTENTS   FORCE_ BIGFILE\r\n--------------- ----------------- ---------- ------- ---------- ------ -------\r\nSYSTEM          LOCAL                   8192 ONLINE  PERMANENT  NO     NO\r\nSYSAUX          LOCAL                   8192 ONLINE  PERMANENT  NO     NO\r\nUNDOTBS1        LOCAL                   8192 ONLINE  UNDO       NO     NO\r\nTEMP            LOCAL                   8192 ONLINE  TEMPORARY  NO     NO\r\nUSERS           LOCAL                   8192 ONLINE  PERMANENT  NO     NO</pre>\r\n<p>表空间改名（必须在open状态下，system , sysaux 不能改名）：</p>\r\n<pre class=\"prettyprint\">ALTER TABLESPACE XXX RENAME TO YYY</pre>\r\n<p>表空间的扩容：</p>\r\n<p>表空间的大小由组成它的所有数据文件的大小之和来决定，扩容的方法：</p>\r\n<p>(1)重置数据文件大小</p>\r\n<pre class=\"prettyprint\">ALTER DATABASE DATAFILE \'...\' | FileNo RESIZE XX</pre>\r\n<p>(2)设置数据文件能自动增长</p>\r\n<pre class=\"prettyprint\">ALTER DATABASE DATAFILE \'... \'| FileNo AUTOEXTEND ON | OFF NEXT 20M MAXSIZE 300M;</pre>\r\n<p>查看哪些表空间为自动增长</p>\r\n<pre class=\"prettyprint\">col FILE_NAME format a40\r\nSELECT FILE_NAME,TABLESPACE_NAME,AUTOEXTENSIBLE FROM dba_data_files;</pre>\r\n<pre class=\"prettyprint\">FILE_NAME                                TABLESPACE_NAME AUTOEX\r\n---------------------------------------- --------------- ------\r\nF:\\ORACLEDB\\ORADATA\\MYDB\\SYSTEM01.DBF    SYSTEM          YES\r\nF:\\ORACLEDB\\ORADATA\\MYDB\\SYSAUX01.DBF    SYSAUX          YES\r\nF:\\ORACLEDB\\ORADATA\\MYDB\\UNDOTBS01.DBF   UNDOTBS1        YES\r\nF:\\ORACLEDB\\ORADATA\\MYDB\\USERS01.DBF     USERS           YES</pre>\r\n<p>(3)添加数据文件</p>\r\n<p>增加到表空间中的数据文件不能直接从表空间中删除，除非删掉整个表空间，增加数据文件将有助于均衡I/O，一个表空间文件最多为个，文件越多，执行一次检查点的代价越高</p>\r\n<pre class=\"prettyprint\">ALTER TABLESPACE tablespace_name  ADD DATAFILE \'...\' SIZE XX;\r\nALTER TABLESPACE tbs2 ADD DATAFILE \'...\' size 200m autoextend on ;</pre>\r\n<p>表空间的四种状态：</p>\r\n<ul>\r\n<li>online</li>\r\n<li>offline</li>\r\n<li>read only</li>\r\n<li>read write</li>\r\n</ul>\r\n<p>offline：</p>\r\n<p>一个表空间的正常状态是联机(ONLINE)，有时需要将某一个表空间进行脱机，以进行数据库维护，表空间脱机的时候，将产生检查点事件，该表空间上的数据是不能访问的。如：</p>\r\n<p>&nbsp; &nbsp; 在数据库打开的状态下移动数据文件<br />&nbsp; &nbsp; 在数据库打开的情况下恢复一个表空间或数据文件<br />&nbsp; &nbsp; 执行表空间的脱机备份（虽然可以进行进行联机备份）<br />&nbsp; &nbsp; 使数据库某一部分不可访问，让其它部分能正常访问。&nbsp;</p>\r\n<p>脱机命令：</p>\r\n<pre class=\"prettyprint\">ALTER TABLESPACE tablespace_name OFFLINE;</pre>\r\n<p>联机命令：</p>\r\n<pre class=\"prettyprint\">ALTER TABLESPACE tablespace_name ONLINE;</pre>\r\n<p>在脱机后，有可能无法再联机，这时要做数据的介质恢复</p>\r\n<pre class=\"prettyprint\">ALTER DATABASE RECOVER TABLESPACE tablespace_name\r\nALTER TABLESPACE tablespace_name ONLINE;</pre>\r\n<p>read only：</p>\r\n<p>不能执行DML语句，可以使用的为DDL，DQL语句。注意，readonly 状态下的表空间可以删除数据，仅仅是不能插入或更新数据。</p>\r\n<pre class=\"prettyprint\">ALTER TABLESPACE tablespace_name READ ONLY;</pre>\r\n<p>read write：</p>\r\n<pre class=\"prettyprint\">ALTER TABLESPACE tablespace_name READ WRITE;</pre>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td colspan=\"3\">一些表空间的特殊状态</td>\r\n</tr>\r\n<tr>\r\n<td style=\"width: 120px;\">tablespacename</td>\r\n<td style=\"width: 100px;\">online</td>\r\n<td style=\"width: 100px;\">read only</td>\r\n</tr>\r\n<tr>\r\n<td>system</td>\r\n<td>必须online</td>\r\n<td>必须read write</td>\r\n</tr>\r\n<tr>\r\n<td>sysaux</td>\r\n<td>可以offline</td>\r\n<td>不能read only</td>\r\n</tr>\r\n<tr>\r\n<td>undo</td>\r\n<td>不能offline</td>\r\n<td>不能read only</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>查看表空间的状态</p>\r\n<pre class=\"prettyprint\">col TABLESPACE_NAME format a18\r\n\r\nSELECT tablespace_name\r\n     , file#\r\n     , v.status\r\n     , v.enabled\r\nFROM\r\n    dba_data_files d,\r\n    v$datafile v\r\nWHERE\r\n    d.file_id = v.file#;</pre>\r\n<pre class=\"prettyprint\">TABLESPACE_NAME         FILE# STATUS         ENABLED\r\n------------------ ---------- -------------- -------------\r\nSYSTEM                      1 SYSTEM         READ WRITE\r\nSYSAUX                      2 ONLINE         READ WRITE\r\nUNDOTBS1                    3 ONLINE         READ WRITE\r\nUSERS                       4 ONLINE         READ WRITE</pre>\r\n<p>表空间数据文件的重命名或重定位</p>\r\n<p>方法一，在open状态下且处于archive模式:</p>\r\n<pre class=\"prettyprint\">--1、查出数据文件位置和所属的表空间\r\nselect TABLESPACE_NAME,FILE_NAME,BYTES/1024/1024 \"SIZE(MB)\"\r\nfrom dba_data_files;\r\n\r\n--2、将数据文件所在的表空间离线\r\nalter tablespace uu offline;\r\n\r\n--3、在操作系统下将文件移动到另外一个位置，或重命名(建议使用cp)\r\nhost mv F:\\ORACLEDB\\ORADATA\\MYDB\\uu.dbf D:\\uu1.dbf\r\n\r\n--4、修改控制文件，将数据文件的指针重新指向到另一个位置(执行rename)\r\nalter tablespace uu rename datafile \r\n    \'F:\\ORACLEDB\\ORADATA\\MYDB\\uu.dbf\' to \'D:\\uu1.dbf\';\r\n\r\n--5、表空间在线\r\nalter tablespace uu online</pre>\r\n<p>方法二，不能脱机的表空间</p>\r\n<pre class=\"prettyprint\">--1、关闭数据库，再启动到mount状态\r\nstartup mount\r\n\r\n--2、在操作系统下，移动或重命名文件\r\nhost mv F:\\ORACLEDB\\ORADATA\\MYDB\\uu.dbf D:\\uu1.dbf\r\n\r\n--3、修改控制文件\r\nalter database rename file \'D:\\uu.dbf\' to \'D:\\uu2.dbf\';\r\n      \r\n--4、打开数据库\r\nalter database open;</pre>\r\n<p>删除表空间：</p>\r\n<p>意味着表空间和数据文件的信息从数据库中删除，同时也应该将相应的操作系统文件删除</p>\r\n<p>不能删除的表空间<br />&nbsp; &nbsp; 系统表空间<br />&nbsp; &nbsp; 有活动回滚段的表空间<br />&nbsp; &nbsp; 默认临时/永久表空间</p>\r\n<pre class=\"prettyprint\">DROP TABLESPACE tablespace_name [INCLUDING CONTENTS [AND DATAFILES]]\r\nINCLUDING CONTENTS                -- 删除表空间及所有段\r\nINCLUDING CONTENTS [AND DATAFILES] --删除表空间、所有段、数据文件、同时删除系统中的物理文件\r\n             \r\n--限制某个用户对表空间的使用：\r\nALTER USER user_name QUOTA n ON tablespace_name;\r\nALTER USER scott QUOTA 10m on UU;\r\n\r\n--取消限制\r\nALTER USER user_name QUOTA UNLIMITED ON tablespace_name;\r\nGRANT UNLIMITED TABLESPACE TO user_name;</pre>\r\n<p>各种表空间的操作：</p>\r\n<pre class=\"prettyprint\">--1、查看有哪些数据文件\r\nSELECT name FROM v$datafile;\r\n \r\n--2、离线\r\nALTER DATABASE DATAFILE \'...\' OFFLINE;\r\n      \r\n--3、在线  \r\nALTER DATABASE DATAFILE \'... \' ONLINE\r\n \r\n--  有可能提示要进行介质恢复：\r\nRECOVER DATAFILE \'...\' ；\r\nALTER DATABASE RECOVER TABLESPACE tablespace_name;\r\n \r\n--  之后再将文件再线\r\n \r\n--重命名数据文件（同前面移动数据文件一样）\r\n \r\n--获取表空间和数据文件信息\r\n--表空间信息\r\ndba_tablespaces\r\nv$tablespace\r\n--数据文件信息\r\ndba_data_files\r\nv$datafile\r\n--临时数据文件信息\r\ndba_temp_files\r\nv$tempfile\r\n      \r\n--查看系统默认的表空间及临时表空间：  \r\nselect PROPERTY_NAME,PROPERTY_VALUE from database_properties\r\nwhere PROPERTY_NAME like \'DEFAULT%\';\r\n \r\n--修改改系统的默认表空间：\r\nSQL&gt;ALTER DATABASE DEFAULT TABLESPACE tablespace_name\r\n \r\n--修改系统的默认临时表空间：\r\nSQL&gt;ALTER DATABASE DEFAULT TEMPORARY TABLESPACE tablespace_name\r\n \r\n--查看用户默认表空间：\r\nselect USERNAME,USER_ID,DEFAULT_TABLESPACE,TEMPORARY_TABLESPACE from dba_users;\r\n \r\n--修改用户默认表空间：\r\nALTER USER user_name DEFAULT TABLESPACE tablespace_name\r\n      \r\n--查看表空间的使用情况：\r\ndba_data_files;\r\ndba_free_space;</pre>\r\n<p><strong>五： 数据文件</strong></p>\r\n<p>数据文件是所有的数据库内容存放的地方，datafile是按照表空间为组织单位，表空间的构成是按照段区块为层次－－－数据文件的逻辑结构，datafile就是表空间的物理文件。datafile中的内容是通过dbwr把data buffer cache中的dirty buffer data 写入的</p>\r\n<p>查看数据文件</p>\r\n<pre class=\"prettyprint\">SQL&gt; column NAME format a38\r\nSQL&gt; column STATUS format a6\r\nSQL&gt; column ENABLED format a10\r\nSQL&gt; select file# ,name,status,enabled,bytes/1024/1024 M from v$datafile;\r\n\r\n     FILE# NAME                                   STATUS ENABLED             M\r\n---------- -------------------------------------- ------ ---------- ----------\r\n         1 F:\\ORACLEDB\\ORADATA\\MYDB\\SYSTEM01.DBF  SYSTEM READ WRITE        700\r\n         2 F:\\ORACLEDB\\ORADATA\\MYDB\\SYSAUX01.DBF  ONLINE READ WRITE    584.125\r\n         3 F:\\ORACLEDB\\ORADATA\\MYDB\\UNDOTBS01.DBF ONLINE READ WRITE        560\r\n         4 F:\\ORACLEDB\\ORADATA\\MYDB\\USERS01.DBF   ONLINE READ WRITE          5\r\n\r\nSQL&gt; select tablespace_name,file_name,user_bytes/1024/1024 M from dba_data_files;\r\nTABLESPACE_NAME    FILE_NAME                                         M\r\n------------------ ---------------------------------------- ----------\r\nSYSTEM             F:\\ORACLEDB\\ORADATA\\MYDB\\SYSTEM01.DBF      699.9375\r\nSYSAUX             F:\\ORACLEDB\\ORADATA\\MYDB\\SYSAUX01.DBF      584.0625\r\nUNDOTBS1           F:\\ORACLEDB\\ORADATA\\MYDB\\UNDOTBS01.DBF     559.9375\r\nUSERS              F:\\ORACLEDB\\ORADATA\\MYDB\\USERS01.DBF         4.9375</pre>\r\n<p>数据文件的增加与删除</p>','',1,1,'2012-01-02 18:06:59','2012-01-02 18:06:59',14,'oracle',NULL),(50,'Oracle常用命令','oracle-common-command',1,'<ol style=\"margin-left: -18px;\">\r\n<li>创建用户\r\n<pre class=\"prettyprint\">create user USERNAME identified by password\r\ndefault tablespace users\r\ntemporary tablespace temp;</pre>\r\n</li>\r\n<li> 删除用户\r\n<pre class=\"prettyprint\">DROP USER username\r\n--如果用户已经有对象了（如TABLE，VIEW等等）要这样：\r\nDROP USER username CASCADE</pre>\r\n</li>\r\n<li>为新创建的用户添加权限， 对于数据库管理员需要授予CONNECT、RESOURCE和DBA角色，对于数据库开发人员需要授予CONNECT和RESOURCE角色即可\r\n<pre class=\"prettyprint\">grant CONNECT, RESOURCE to USERNAME</pre>\r\n</li>\r\n<li>修改用户密码：\r\n<pre class=\"prettyprint\">/*用OS认证方式登陆，即是CONNECT / AS SYSDBA 登陆*/\r\nALTER USER username IDENTIFIED BY ****;</pre>\r\n</li>\r\n<li> 查看所有用户\r\n<pre class=\"prettyprint\">column USERNAME FORMAT A25\r\ncolumn DEFAULT_TABLESPACE FORMAT A20\r\ncolumn TEMPORARY_TABLESPACE FORMAT A21\r\n\r\nSELECT USERNAME\r\n     , DEFAULT_TABLESPACE\r\n     , TEMPORARY_TABLESPACE\r\nFROM\r\n    dba_users\r\nORDER BY\r\n    username ASC;</pre>\r\n</li>\r\n<li>查看表空间使用的管理方式：\r\n<pre class=\"prettyprint\">col TABLESPACE_NAME format a15\r\ncol EXTENT_MANAGEMENT format a17\r\ncol STATUS format a7\r\ncol CONTENTS format a10\r\n\r\nSELECT TABLESPACE_NAME\r\n     , EXTENT_MANAGEMENT\r\n     , BLOCK_SIZE\r\n     , STATUS\r\n     , CONTENTS\r\n     , FORCE_LOGGING\r\n     , BIGFILE\r\nFROM\r\n    dba_tablespaces;</pre>\r\n</li>\r\n<li>查看表空间及数据文件\r\n<pre class=\"prettyprint\">column TS_NAME FORMAT A15\r\ncolumn DF_NAME FORMAT A50\r\n\r\nSELECT t1.NAME AS TS_NAME\r\n     , t2.NAME AS DF_NAME\r\nFROM\r\n    v$tablespace t1, v$datafile t2 \r\nWHERE\r\n    t1.ts# = t2.ts#;</pre>\r\n</li>\r\n<li>查看所有用户表\r\n<pre class=\"prettyprint\">/*查看所有用户表*/\r\nSELECT table_name FROM user_tables; \r\n/*查看属于某个用户的表*/\r\nSELECT table_name FROM dba_tables WHERE owner=\'USERNAME\';</pre>\r\n</li>\r\n<li> oracle带参数的存储过程的执行\r\n<pre class=\"prettyprint\">CREATE OR REPLACE PROCEDURE YWJ.CHECK_SESSION \r\n(\r\n    SESSION_ID in varchar2,\r\n    RECOGNIZE_FLG out char,\r\n    ERROR_CODE out number\r\n)\r\nIS\r\nBEGIN\r\n    IF SESSION_ID = \'1\' THEN\r\n        BEGIN\r\n            RECOGNIZE_FLG := 0;\r\n            ERROR_CODE := 1;\r\n        END;\r\n    END IF;\r\nEND ;\r\n\r\nSQL&gt; var a char(1);\r\nSQL&gt; var b number;\r\nSQL&gt; exec CHECK_SESSION(\'1\', :a, :b)\r\n\r\nSQL&gt; print a\r\n\r\nA\r\n---------------------\r\n0</pre>\r\n</li>\r\n<li> 创建表\r\n<pre class=\"prettyprint\">CREATE TABLE IT_Employees\r\n(\r\n Employee_ID NUMBER(6) NOT NULL UNIQUE,\r\n Frist_Name VARCHAR2(20),\r\n Last_Name VARCHAR2(25) NOT NULL,\r\n EMail VARCHAR2(25),\r\n Phone_Number VARCHAR2(20),\r\n Job_ID VARCHAR2(10),\r\n Salary NUMBER(8,2),\r\n Manager_ID NUMBER(6)\r\n);</pre>\r\n</li>\r\n<li> 创建视图\r\n<pre class=\"prettyprint\">CREATE VIEW Prog_Employees_2\r\nAS\r\nSELECT Employee_ID, First_Name, Last_Name, EMail,\r\nPhone_Number, Salary Manager_ID\r\nFROM IT_Employees\r\nWHERE Job_ID = \'IT_PROG\'\r\nWITH CHECK OPTION;</pre>\r\nWITH CHECK OPTION 表示对视力进行UPDATE、INSERT和DELETE操作时，要保证更新、插入或删除的行满足视图定义中的谓词条件（即子查询中的条件表达式）</li>\r\n<br />\r\n<li> 创建索引\r\n<pre class=\"prettyprint\">CREATE [UNIQUE] INDEX Index_Name\r\nON Table_Name(Col1, Col2);</pre>\r\n</li>\r\n<li> 删除表\r\n<pre class=\"prettyprint\">DROP TABLE IT_Employees;</pre>\r\n注意：在Oracle中删除基表后建立在此表上的视力定义仍然保留在数据字典中，而不当用户引用该视图时会报错，这时需要再对视图进行单独删除</li>\r\n<br />\r\n<li>修改表\r\n<pre class=\"prettyprint\">ALTER TABLE IT_Employees ADD Birth_Date DATE;  --添加列\r\nALTER TABLE IT_Employees MODIFY Manager_ID NUMBER(8); -- 修改列定义\r\nALTER TABLE IT_Employees RENAME COLUMN Manager_ID TO Manager_ID2 --修改列名\r\nALTER TABLE IT_Employees DROP UNIQUE(Employee_ID);  --删除约束\r\nALTER TABLE IT_Employees DROP COLUMN Birth_Date  -- 删除列\r\nRENAME IT_Employees TO IT_Employees2;  --修改表名\r\nCOMMENT ON TABLE IT_Employees2 IS \'员工表\'  --添加表注释;\r\nCOMMENT ON COLUMN IT_Employees2.Employee_ID IS \'员工ID\';  --添加列注释</pre>\r\n</li>\r\n<li>UNION, UNION ALL, INTERSECT, MINUS<br /><span style=\"color: #ff00ff;\"> UNION</span>：将多个查询结果集相加，形成一个结果集，等同于集合运算中的并运算。即UNION运算符可以将第一个查询中的所有行与第二个查询中的所有行相加，并消除其中重复的行形成一个合集<br /><span style=\"color: #ff00ff;\">UNION ALL</span>：UNION ALL与UNION语句的工作方式基本相同，不同之处是UNION ALL操作符形成的结果集中包含有两个子结果集中重复的行<br /><span style=\"color: #ff00ff;\">INTERSECT</span>：该操作符也用于对两个SQL语句的产生的结果集进行处理。不同之处是UNION基本上是一个OR运算，而INTERSECT则比较像AND，即UNION是并集运算，而INTERSECT是交集运算，查询的结果是两个结果集中都存在的值。<br /><span style=\"color: #ff00ff;\">MINUS</span>：该运算符可以找到两个给定的集合之间的差集，也就是说该集合操作符会返回所有从第一个查询中返回的，但是没有在第二个查询中返回的记录。&nbsp;<br />注意：在使用集合操作符编写复合查询时，其规则包括：第一、在构成复合查询的各个查询中，各SELECT语句指定的列必须在数量上和数据类型上相匹配；第二、不允许在构成复合查询的各个查询中规定ORDER BY子句；第三、不允许在BLOB、LONG这样的大数据类型对象上使用集合操作符。&nbsp;</li>\r\n</ol>','',1,1,'2012-01-05 23:35:00','2012-01-05 23:35:00',14,'oracle',NULL),(51,'Asp.NET中配置log4net','aspnet-log4net',1,'<p><strong>一： 什么是log4net</strong></p>\r\n<p>log4net(Log For Net)是Apache开源的应用于.Net框架的日志记录工具,详细信息参见Apache网站.它是针对Java的log4j(Log For Java的)姊妹工具，首先你应该下载log4net.dll并引入到你的项目References中,或者把源代码项目作为你工程的一部分加入到你的工程当中。地址如下：<a href=\"http://logging.apache.org/log4net/download.html\">http://logging.apache.org/log4net/download.html</a>，目前的最新版本是1.2.11</p>\r\n<p>使用log4net需要知道其中两个比较重要的概念:logger 和 appender。前者是日志记录对象,后者是日志记录的目标，包括控制台,文件,定量大小的文件,远程广播。也就是说我们使用log4net的过程可以是这样的:获得一个用来记录日志的工具对象logger,然后为logger对象指定日志的记录位置.美妙的是,这一切工作都可以在Web.config配置文件中完成,所用的代码极少.&nbsp;</p>\r\n<p><strong>二： log4net在Asp.net中的配置</strong></p>\r\n<p>1. 先在web.config中的&lt;configSections&gt;节点下添加如下配置：</p>\r\n<pre class=\"prettyprint\">&lt;configSections&gt;\r\n  &lt;section name=\"log4net\" \r\n    type=\"log4net.Config.Log4NetConfigurationSectionHandler, log4net\" /&gt;\r\n&lt;/configSections&gt;</pre>\r\n<p>2. 接下来在&lt;configuration&gt;节点下添加如下log4net节点配置：</p>\r\n<pre class=\"prettyprint\">&lt;log4net&gt;\r\n  &lt;appender name=\"ERROR\" type=\"log4net.Appender.FileAppender\"&gt;\r\n    &lt;param name=\"File\" value=\"log/error.log\" /&gt;\r\n    &lt;param name=\"datePattern\" value=\"yyyy-MM-dd HH:mm\" /&gt;\r\n    &lt;param name=\"AppendToFile\" value=\"true\" /&gt;\r\n    &lt;layout type=\"log4net.Layout.PatternLayout\"&gt;\r\n      &lt;param name=\"ConversionPattern\" value=\"[%d] - %m%n\" /&gt;\r\n    &lt;/layout&gt;\r\n    &lt;filter type=\"log4net.Filter.LevelRangeFilter\"&gt;\r\n      &lt;param name=\"LevelMin\" value=\"ERROR\"/&gt;\r\n      &lt;param name=\"LevelMax\" value=\"ERROR\"/&gt;\r\n    &lt;/filter&gt;\r\n  &lt;/appender&gt;\r\n  &lt;appender name=\"INFO\" type=\"log4net.Appender.FileAppender\"&gt;\r\n    &lt;param name=\"File\" value=\"log/info.log\" /&gt;\r\n    &lt;param name=\"datePattern\" value=\"yyyy-MM-dd HH:mm\" /&gt;\r\n    &lt;param name=\"AppendToFile\" value=\"true\" /&gt;\r\n    &lt;layout type=\"log4net.Layout.PatternLayout\"&gt;\r\n      &lt;param name=\"ConversionPattern\" value=\"[%d] - %m%n\" /&gt;\r\n    &lt;/layout&gt;\r\n    &lt;filter type=\"log4net.Filter.LevelRangeFilter\"&gt;\r\n      &lt;param name=\"LevelMin\" value=\"INFO\"/&gt;\r\n      &lt;param name=\"LevelMax\" value=\"INFO\"/&gt;\r\n    &lt;/filter&gt;\r\n  &lt;/appender&gt;\r\n\r\n  &lt;root&gt;\r\n    &lt;level value=\"DEBUG\" /&gt;\r\n    &lt;appender-ref ref=\"ERROR\"/&gt;\r\n    &lt;appender-ref ref=\"INFO\"/&gt;\r\n    &lt;appender-ref ref=\"DEBUG\"/&gt;\r\n  &lt;/root&gt;\r\n&lt;/log4net&gt;</pre>\r\n<p>从上面的配置中可以看到定义了多个appender，每一个appender就是一个日志记录的目标。root节点指定了选用哪一个 appender. 在这里我选用了LogFileAppender。</p>\r\n<p>3. 初始化</p>\r\n<p>到这里配置就基本上完成了，但是怎么使用上面的配置呢？log4net为我们提供了在应用程序启动时加载配置信息的入口，很简单，在Global.asax.cs的Application_Start过程中添加如下代码：</p>\r\n<pre class=\"prettyprint\">log4net.Config.DOMConfigurator.Configure();</pre>\r\n<p>好了，到此为止整个配置过程完成了，下面是怎么调用的问题了。</p>\r\n<p>4. 调用log4net对象生成日志</p>\r\n<p>新建一个log4nettest.aspx页面,在cs文件中添加引用 using log4net;定义本页的一个全局变量:</p>\r\n<pre class=\"prettyprint\">private static readonly ILog log = LogManager.GetLogger(typeof(log4nettest));\r\nlog.Debug(\"这是我在使用Log4Net\");</pre>\r\n<p>打开&ldquo;D:\\Applog.txt&rdquo;就会看到生成的日志信息</p>\r\n<p><strong>三： log4net的结构</strong></p>\r\n<p>log4net 有四种主要的组件，分别是Logger（记录器）, Repository（库）, Appender（附着器）以及 Layout（布局）。</p>\r\n<p><span style=\"color: #ff00ff;\">3.1 Logger接口</span></p>\r\n<p>Logger是应用程序需要交互的主要组件，它用来产生日志消息。产生的日志消息并不直接显示，还要预先经过Layout的格式化处理后才会输出。</p>\r\n<p>Logger提供了多种方式来记录一个日志消息，你可以在你的应用程序里创建多个Logger，每个实例化的Logger对象都被log4net框架作为命名实体(named entity)来维护。这意味着为了重用Logger对象，你不必将它在不同的类或对象间传递，只需要用它的名字为参数调用就可以了。log4net框架使用继承体系，继承体系类似于.NET中的名字空间。也就是说，如果有两个logger,分别被定义为a.b.c和a.b，那么我们说a.b是a.b.c的祖先。每一个logger都继承了祖先的属性</p>\r\n<p>Log4net框架定义了一个ILog接口，所有的logger类都必须实现这个接口。如果你想实现一个自定义的logger，你必须首先实现这个接口。你可以参考在/extension目录下的几个例子。</p>\r\n<p>ILog接口的定义如下：</p>\r\n<pre class=\"prettyprint\">public interface ILog\r\n{\r\n    void Debug(object message);\r\n    void Info(object message);\r\n    void Warn(object message);\r\n    void Error(object message);\r\n    void Fatal(object message);\r\n \r\n    //以上的每一个方法都有一个重载的方法，用来支持异常处理。\r\n    //每一个重载方法都如下所示，有一个异常类型的附加参数。\r\n    void Debug(object message, Exception ex);\r\n    // ...\r\n \r\n    //Boolean 属性用来检查Logger的日志级别\r\n    //（我们马上会在后面看到日志级别）\r\n    bool isDebugEnabled;\r\n    bool isInfoEnabled;\r\n    //&hellip; 其他方法对应的Boolean属性\r\n}</pre>\r\n<p>Log4net框架定义了一个叫做LogManager的类，用来管理所有的logger对象。它有一个GetLogger()静态方法，用我们提供的名字参数来检索已经存在的Logger对象。如果框架里不存在该Logger对象，它也会为我们创建一个Logger对象。代码如下所示：</p>\r\n<pre class=\"prettyprint\">log4net.ILog log = log4net.LogManager.GetLogger(\"logger-name\");</pre>\r\n<p>通常来说，我们会以类（class）的类型（type）为参数来调用GetLogger()，以便跟踪我们正在进行日志记录的类。传递的类(class)的类型(type)可以用typeof(Classname)方法来获得，或者可以用如下的反射方法来获得：</p>\r\n<pre class=\"prettyprint\">System.Reflection.MethodBase.GetCurrentMethod().DeclaringType</pre>\r\n<p>尽管符号长了一些，但是后者可以用于一些场合，比如获取调用方法的类(class)的类型(type)。</p>\r\n<p><span style=\"color: #ff00ff;\">3.2 日志的级别</span></p>\r\n<p>正如你在ILog的接口中看到的一样，有五种不同的方法可以跟踪一个应用程序。事实上，这五种方法是运作在Logger对象设置的不同日志优先级别上。这几种不同的级别是作为常量定义在log4net.spi.Level类中。你可以在程序中使用任何一种方法。但是在最后的发布中你也许不想让所有的代码来浪费你的CPU周期，因此，框架提供了7种级别和相应的Boolean属性来控制日志记录的类型。</p>\r\n<p>Level有以下几种取值</p>\r\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"2\">\r\n<tbody>\r\n<tr>\r\n<th style=\"width: 70px;\">级别</th> <th style=\"width: 150px;\">允许的方法</th> <th style=\"width: 220px;\">Boolean属性</th> <th>优先级别</th>\r\n</tr>\r\n<tr>\r\n<td>OFF</td>\r\n<td>&nbsp;</td>\r\n<td>&nbsp;</td>\r\n<td>Highest</td>\r\n</tr>\r\n<tr>\r\n<td>FATAL</td>\r\n<td>void Fatal(...);</td>\r\n<td>bool IsFatalEnabled;</td>\r\n<td>&nbsp;</td>\r\n</tr>\r\n<tr>\r\n<td>RROR</td>\r\n<td>void Error(...);</td>\r\n<td>bool IsErrorEnabled;</td>\r\n<td>&nbsp;</td>\r\n</tr>\r\n<tr>\r\n<td>WARN</td>\r\n<td>void Warn(...);</td>\r\n<td>bool IsWarnEnabled;</td>\r\n<td>&nbsp;</td>\r\n</tr>\r\n<tr>\r\n<td>INFO</td>\r\n<td>void Info(...);</td>\r\n<td>bool IsInfoEnabled;</td>\r\n<td>&nbsp;</td>\r\n</tr>\r\n<tr>\r\n<td>DEBUG</td>\r\n<td>void Debug(...);</td>\r\n<td>bool IsDebugEnabled;</td>\r\n<td>&nbsp;</td>\r\n</tr>\r\n<tr>\r\n<td>ALL</td>\r\n<td>&nbsp;</td>\r\n<td>&nbsp;</td>\r\n<td>Lowest</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>在log4net框架里，通过设置配置文件，每个日志对象都被分配了一个日志优先级别。如果没有给一个日志对象显式地分配一个级别，那么该对象会试图从他的祖先继承一个级别值。</p>\r\n<p>ILog接口的每个方法都有一个预先定义好了的级别值。正如你在表1看到的，ILog的Inof()方法具有INFO级别。同样的，以此类推，Error()方法具有ERROR级别。当我们使用以上的任何一种方法时，log4net框架会检查日志对象logger的级别和方法的级别。只有当方法的级别高于日志级别时，日志请求才会被接受并执行。</p>\r\n<p>举例说明，当你创建了一个日志对象，并且把他的级别设置为INFO。于是框架会设置日志的每个Boolean属性。当你调用相应的日志方法时，框架会检查相应的Boolean属性，以决定该方法能不能执行。如下的代码：</p>\r\n<pre class=\"prettyprint\">Logger.Info(\"message\");\r\nLogger.Debug(\"message\");\r\nLogger.Warn(\"message\");</pre>\r\n<p>对于第一种方法，Info()的级别等与日志的级别（INFO），因此日志请求会被传递，我们可以得到输出结果&rdquo;message&rdquo;。<br />对于第二种方法，Debug()的级别低于日志对象logger的日志级别(INFO)，因此，日志请求被拒绝了，我们得不到任何输出。同样的，针对第三行语句，我们可以很容易得出结论。</p>\r\n<p>在表1中有两个特殊的级别：ALL和OFF。ALL表示允许所有的日志请求。OFF是拒绝所有的请求。你也可以显式地检查Logger对象的Boolean属性，如下所示：</p>\r\n<pre class=\"prettyprint\">if (logger.IsDebugEnabled)\r\n{\r\n    Logger.Debug(\"message\");\r\n}</pre>\r\n<p><span style=\"color: #ff00ff;\">3.3 Repository&nbsp;</span></p>\r\n<p>Repository主要用于负责日志对象组织结构的维护。在log4net的以前版本中，框架仅支持分等级的组织结构(hierarchical organization)。这种等级结构本质上是库的一个实现，并且定义在log4net.Repository.Hierarchy 名字空间中。要实现一个Repository，需要实现log4net.Repository.ILoggerRepository 接口。但是通常并不是直接实现该接口，而是以log4net.Repository.LoggerRepositorySkeleton为基类继承。体系库 (hierarchical repository )则由log4net.Repository.Hierarchy.Hierarchy类实现。</p>\r\n<p>如果你是个log4net框架的使用者，而非扩展者，那么你几乎不会在你的代码里用到Repository的类。相反的，你需要用到LogManager类来自动管理库和日志对象。</p>\r\n<p><span style=\"color: #ff00ff;\">3.4 Appender</span></p>\r\n<p>一个好的日志框架应该能够产生多目的地的输出。比如说输出到控制台或保存到一个日志文件。log4net 能够很好的满足这些要求。它使用一个叫做Appender的组件来定义输出介质。正如名字所示，这些组件把它们附加到Logger日志组件上并将输出传递到输出流中。你可以把多个Appender组件附加到一个日志对象上。 Log4net框架提供了几个Appender组件。关于log4net提供的Appender组件的完整列表可以在log4net框架的帮助手册中找到。有了这些现成的Appender组件，一般来说你没有必要再自己编写了。但是如果你愿意，可以从log4net.Appender.AppenderSkeleton类继承。</p>\r\n<p><span style=\"color: #ff00ff;\">3.5 Appender Filters</span></p>\r\n<p>一个Appender 对象缺省地将所有的日志事件传递到输出流。Appender的过滤器(Appender Filters) 可以按照不同的标准过滤日志事件。在log4net.Filter的名字空间下已经有几个预定义的过滤器。使用这些过滤器，你可以按照日志级别范围过滤日志事件，或者按照某个特殊的字符串进行过滤。你可以在API的帮助文件中发现更多关于过滤器的信息。</p>\r\n<p><span style=\"color: #ff00ff;\">3.6 Layout</span></p>\r\n<p>Layout 组件用于向用户显示最后经过格式化的输出信息。输出信息可以以多种格式显示，主要依赖于我们采用的Layout组件类型。可以是线性的或一个XML文件。Layout组件和一个Appender组件一起工作。API帮助手册中有关于不同Layout组件的列表。一个Appender对象，只能对应一个Layout对象。要实现你自己的Layout类，你需要从log4net.Layout.LayoutSkeleton类继承，它实现了ILayout接口。</p>\r\n<p><strong>四： 配置文件的说明</strong></p>\r\n<p>4.1 &lt;root&gt;</p>\r\n<pre class=\"prettyprint\">&lt;root&gt;\r\n  &lt;level value=\"DEBUG\" /&gt;\r\n  &lt;appender-ref ref=\"ERROR\"/&gt;\r\n  &lt;appender-ref ref=\"INFO\"/&gt;\r\n  &lt;appender-ref ref=\"DEBUG\"/&gt;\r\n&lt;/root&gt;</pre>\r\n<p>在框架的体系里，所有的日志对象都是根日志(root logger)的后代。 因此如果一个日志对象没有在配置文件里显式定义，则框架使用根日志中定义的属性。在&lt;root&gt;标签里，可以定义level级别值和Appender的列表。如果没有定义LEVEL的值，则缺省为DEBUG。可以通过&lt;appender-ref&gt;标签定义日志对象使用的Appender对象。&lt;appender-ref&gt;声明了在其他地方定义的Appender对象的一个引用。在一个logger对象中的设置会覆盖根日志的设置。而对Appender属性来说，子日志对象则会继承父日志对象的Appender列表。这种缺省的行为方式也可以通过显式地设定&lt;logger&gt;标签的additivity属性为false而改变。</p>\r\n<pre class=\"prettyprint\">&lt;logger name=\"testApp.Logging\" additivity=\"false\"&gt;\r\n&lt;/logger&gt;</pre>\r\n<p>Additivity的值缺省是true.</p>\r\n<p>4.2 &lt;logger&gt;</p>\r\n<pre class=\"prettyprint\">&lt;logger name=\"testApp.Logging\"&gt;\r\n  &lt;level value=\"DEBUG\"/&gt;\r\n&lt;/logger&gt;</pre>\r\n<p>&lt;logger&gt; 元素预定义了一个具体日志对象的设置。然后通过调用LogManager.GetLogger(&ldquo;testAPP.Logging&rdquo;)函数，你可以检索具有该名字的日志。如果LogManager.GetLogger(&hellip;)打开的不是预定义的日志对象，则该日志对象会继承根日志对象的属性。知道了这一点，我们可以说，其实&lt;logger&gt;标签并不是必须的。</p>\r\n<p>4.3 &lt;appender&gt;</p>\r\n<pre class=\"prettyprint\">&lt;appender name=\"LogFileAppender\"\r\n          type=\"log4net.Appender.FileAppender\" &gt;\r\n  &lt;param name=\"File\" value=\"log-file.txt\" /&gt;\r\n  &lt;param name=\"AppendToFile\" value=\"true\" /&gt;\r\n  &lt;layout type=\"log4net.Layout.PatternLayout\"&gt;\r\n    &lt;param name=\"Header\" value=\"[Header]\\r\\n\" /&gt;\r\n    &lt;param name=\"Footer\" value=\"[Footer]\\r\\n\"/&gt;\r\n    &lt;param name=\"ConversionPattern\"\r\n      value=\"%d [%t] %-5p %c - %m%n\"\r\n    /&gt;\r\n  &lt;/layout&gt;\r\n  &lt;filter type=\"log4net.Filter.LevelRangeFilter\"&gt;\r\n    &lt;param name=\"LevelMin\" value=\"DEBUG\" /&gt;\r\n    &lt;param name=\"LevelMax\" value=\"WARN\" /&gt;\r\n  &lt;/filter&gt;\r\n&lt;/appender&gt;</pre>\r\n<p>在&lt;root&gt;标签或单个的&lt;logger&gt;标签里的Appender对象可以用&lt;appender&gt;标签定义。&lt;appender&gt;标签的基本形式如上面所示。它定义了appender的名字和类型。 另外比较重要的是&lt;appender&gt;标签内部的其他标签。不同的appender有不同的&lt;param&gt;标签。在这里，为了使用FileAppender,你需要一个文件名作为参数。另外还需要一个在&lt;appender&gt;标签内部定义一个Layout对象。Layout对象定义在它自己的&lt;layout&gt;标签内。&lt;layout&gt;标签的type属性定义了Layout的类型(在本例里是PatternLayout)，同时也确定了需要提供的参数值。Header和Footer标签提供了一个日志会话(logging session)开始和结束时输出的文字。有关每种appender的具体配置的例子，可以在log4net\\doc\\manual\\example-config-appender.html中得到。</p>\r\n<p>4.4 log4net.Layout.PatternLayout中的转换模式(ConversionPattern)</p>\r\n<table border=\"1\" cellspacing=\"0\" cellpadding=\"2\">\r\n<tbody>\r\n<tr>\r\n<td>%m(message)</td>\r\n<td>输出的日志消息，如ILog.Debug(&hellip;)输出的一条消息</td>\r\n</tr>\r\n<tr>\r\n<td>%n(new line)</td>\r\n<td>换行</td>\r\n</tr>\r\n<tr>\r\n<td>%d(datetime)</td>\r\n<td>输出当前语句运行的时刻</td>\r\n</tr>\r\n<tr>\r\n<td>%r(run time)</td>\r\n<td>输出程序从运行到执行到当前语句时消耗的毫秒数</td>\r\n</tr>\r\n<tr>\r\n<td>%t(thread id)</td>\r\n<td>%t(thread id)</td>\r\n</tr>\r\n<tr>\r\n<td>%p(priority)</td>\r\n<td>日志的当前优先级别，即DEBUG、INFO、WARN&hellip;等</td>\r\n</tr>\r\n<tr>\r\n<td>%c(class)</td>\r\n<td>当前日志对象的名称，例如：<br />模式字符串为：%-10c -%m%n<br />代码为：<br />则输出为下面的形式：<br />Exam.Log       - Hello</td>\r\n</tr>\r\n<tr>\r\n<td>%L</td>\r\n<td>输出语句所在的行号</td>\r\n</tr>\r\n<tr>\r\n<td>%F</td>\r\n<td>输出语句所在的文件名</td>\r\n</tr>\r\n<tr>\r\n<td>%-数字</td>\r\n<td>表示该项的最小长度，如果不够，则用空格填充，例如：<br />转换模式为%r [%t]%-5p %c - %m%n 的 PatternLayout 将生成类似于以下内容的输出：<br />176 [main] INFO  org.foo.Bar - Located nearest gas station.</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>4.5 &lt;filter&gt;</p>\r\n<p>最后，让我们看看在Appender元素里的&lt;filter&gt;标签。它定义了应用到Appender对象的过滤器。本例中，我们使用了LevelRangeFilter过滤器,它可以只记录LevelMin和LevelMax参数指定的日志级别之间的日志事件。可以在一个Appender上定义多个过滤器（Filter）,这些过滤器将会按照它们定义的顺序对日志事件进行过滤。其他过滤器的有关信息可以在log4net的SDK文档中找到。</p>','',1,1,'2012-01-11 23:38:32','2012-01-11 23:38:32',7,'asp.net, log4net',NULL),(52,'VB.NET发送邮件','vbnet-send-mail',1,'<p>直接上代码：</p>\r\n<pre class=\"prettyprint\">Imports System.Net.Mail\r\n\r\nPartial Public Class _Default\r\n    Inherits System.Web.UI.Page\r\n\r\n    Protected Sub Page_Load(ByVal sender As Object\r\n            , ByVal e As System.EventArgs) Handles Me.Load\r\n        Dim client As New SmtpClient()\r\n        client.Host = \"smtp.163.com\"\r\n        client.UseDefaultCredentials = False\r\n        client.Credentials = New System.Net.NetworkCredential(\r\n            \"ywjsailor@163.com\", \"finalsquall\")\r\n        client.DeliveryMethod = SmtpDeliveryMethod.Network\r\n\r\n        Dim message As New MailMessage(\"ywjsailor@163.com\"\r\n            , \"ywjsailor@gmail.com\")\r\n        message.Subject = \"主题测试\"\r\n        message.Body = \"asp.net发送邮件\"\r\n        message.BodyEncoding = System.Text.Encoding.UTF8\r\n        message.IsBodyHtml = True\r\n        \'添加附件需将(附件先上传到服务器) \'\r\n        \'Dim data As New System.Net.Mail.Attachment(\"Upload/File1.rar\", \'\r\n        \'   System.Net.Mime.MediaTypeNames.Application.Octet) \'\r\n        \'message.Attachments.Add(Data)\'\r\n        Try\r\n            client.Send(message)\r\n            Response.Write(\"Email successfully send.\")\r\n        Catch ex As Exception\r\n            Response.Write(\"Send Email Failed.\" + ex.ToString())\r\n        End Try\r\n    End Sub\r\n\r\nEnd Class</pre>\r\n<p>程序说明：</p>\r\n<p>client.Host： 获取或设置用于 SMTP 事务的主机的名称或 IP 地址。</p>\r\n<p>client.UseDefaultCredentials： 获取或设置 Boolean 值，该值控制 DefaultCredentials 是否随请求一起发送。有些 SMTP 服务器要求在代表客户端发送电子邮件前验证客户端的身份。当此 SmtpClient 对象应该使用当前登录用户的默认凭据进行身份验证（如果服务器要求）时，请将此属性设置为 true。对于客户端应用程序，这是在大多数情况下都需要的行为。</p>\r\n<p>client.Credentials： 获取或设置用于验证发件人身份的凭据。</p>\r\n<p>client.DeliveryMethod： 指定如何处理待发的电子邮件。</p>','',1,1,'2012-01-14 00:50:20','2012-01-14 00:50:20',7,'VB.NET',NULL),(53,'GAE中的django','django-in-gae',1,'<p>在用GAE实现获取多个同名的复选框的值的操作时, 根据Django的方法很自然的写成这样:</p>\r\n<pre class=\"prettyprint\">self.request.post.getlist(\'key\')</pre>\r\n<p>但是在GAE中就错了, 提示方法找不到, 看来根据经验的类比往往很杯具, 而这种用法在GAE官方文档的例子中似乎并未提及, 只好翻看GAE的代码了, 果然在GAE中已经替换为get_all了.</p>\r\n<pre class=\"prettyprint\">self.reqeust.get_all(\'key\')</pre>','',1,1,'2012-02-12 22:28:18','2012-02-12 22:28:18',2,'gae, django',NULL),(54,'Ubuntu下搭建Django开发环境','ubuntu-install-django',1,'<p>1. 环境</p>\r\n<p>Ubuntu12.04 + django1.4 + mysql5.5 + MySQLdb</p>\r\n<p>2. 安装setuptools</p>\r\n<p>到<a href=\"http://pypi.python.org/pypi/setuptools\">http://pypi.python.org/pypi/setuptools</a>下载setuptools-0.6c11-py2.7.egg。运行如下命令进行安装</p>\r\n<pre class=\"prettyprint\">sh setuptools-0.6c11-py2.7.egg</pre>\r\n<p>3. 安装django</p>\r\n<pre class=\"prettyprint\">sudo easy_install django</pre>\r\n<p>4. 安装mysql</p>\r\n<pre class=\"prettyprint\">sudo apt-get install mysql-server\r\nsudo apt-get install mysql-workbench</pre>\r\n<p>安装完成后会需要修改mysql的编码</p>\r\n<pre class=\"prettyprint\">sudo gedit /etc/mysql/my.cnf\r\n\r\n＃ 找到[client] 添加:\r\ndefault-character-set=utf8\r\n\r\n＃ 找到[mysqld] 添加:\r\ndefault-character-set=utf8</pre>\r\n<p>5. 安装MySQLdb</p>\r\n<pre class=\"prettyprint\">sudo easy_install python-mysql</pre>\r\n<p>出错解决办法：</p>\r\n<p>1. ImportError: No module named _mysql，执行如下命令即可：</p>\r\n<pre class=\"prettyprint\">sudo apt-get install python-mysqldb</pre>\r\n<p>&nbsp;</p>','',1,1,'2012-04-29 11:47:18','2012-04-29 11:47:18',3,'django',2),(55,'Ubuntu安装PyQt','ubuntu-install-pyqt',1,'<p>1. 环境</p>\r\n<p>Ubuntu12.04 + PyQt4</p>\r\n<p>2. 只需要运行如下两个命令即可</p>\r\n<pre class=\"prettyprint\">sudo apt-get install libqt4-dev\r\nsudo apt-get install python-qt4</pre>','',1,1,'2012-04-29 12:04:00','2012-04-29 12:04:00',19,'pyqt',2),(56,'Ubuntu下用VIM开发Python','ubuntu-vim-python',1,'<p>1. 安装VIM</p>\r\n<pre class=\"prettyprint\">sudo apt-get install vim vim-doc vim-scripts</pre>\r\n<p>其中vim-scripts是vim的一些基本插件，包括语法高亮的支持、缩进等等。</p>\r\n<p>2. VIM配置文件</p>\r\n<p>Vim强大的功能，其来源基本上就两个地方：插件，以及配置文件。</p>\r\n<p>上面已经下载了Vim的基本插件，下面说一下Vim的基本配置。Vim本身的系统配置文件夹是在/usr/share/vim/和/etc/vim /两个文件夹下，我们一般不要去改变这些，改了以后不容易恢复。我们需要在用户文件夹下建立自己的配置文件。假设用户的名字是user。进入用户文件夹(/home/user/)之后，用gedit新建一个名叫.vimrc的文件：</p>\r\n<pre class=\"prettyprint\">gedit .vimrc</pre>\r\n<p>然后把下列文件拷贝进这个文件后保存</p>\r\n<pre class=\"prettyprint\">\" 自动补全命令时候使用菜单式匹配列表\r\nset wildmenu\r\n\" 允许退格键删除\r\nset backspace=2\r\n\" 启用鼠标\r\nset mouse=a\r\n\" 文件类型\r\nfiletype on\r\nfiletype plugin on\r\nfiletype indent on\r\n\" 设置编码自动识别, 中文引号显示\r\n\"set fileencodings=utf-8,cp936,big5,euc-jp,euc-kr,latin1,ucs-bom\r\nset fileencodings=utf-8,gbk\r\nset ambiwidth=double\r\n\" 设置缩进为4个空格\r\nautocmd FileType python setlocal et sta sw=4 sts=4\r\n\r\n\" 移动长行\r\nnnoremap &lt;Down&gt; gj\r\nnnoremap &lt;Up&gt; gk\r\n\r\n\" 高亮\r\nsyntax on\r\n\" 设置高亮搜索\r\nset hlsearch\r\n\" 输入字符串就显示匹配点\r\nset incsearch\r\n\" 输入的命令显示出来，看的清楚些。\r\nset showcmd\r\n\r\n\" 打开当前目录文件列表\r\nmap &lt;F3&gt; :e .&lt;CR&gt;\r\n\r\n\" Taglist\r\nlet Tlist_File_Fold_Auto_Close=1\r\nset updatetime=1000\r\nmap &lt;F4&gt; :Tlist&lt;CR&gt;\r\n\r\n\" 按 F8 智能补全\r\ninoremap &lt;F8&gt; &lt;C-x&gt;&lt;C-o&gt;\r\n\r\n\" vim 自动补全 Python 代码\r\n\" 来自http://vim.sourceforge.net/scripts/script.php?script_id=850\r\nautocmd FileType python set complete+=k~/.vim/tools/pydiction\r\n\" 自动使用新文件模板\r\nautocmd BufNewFile *.py 0r ~/.vim/template/simple.py\r\n\r\n\"Format the statusline\r\n\"Nice statusbar\r\nset laststatus=2\r\nset statusline=\r\nset statusline+=%2*%-3.3n%0*\\ \" buffer number\r\nset statusline+=%f\\ \" file name\r\nset statusline+=%h%1*%m%r%w%0* \" flag\r\nset statusline+=[\r\nif v:version &gt;= 600\r\nset statusline+=%{strlen(&amp;ft)?&amp;ft:\'none\'}, \" filetype\r\nset statusline+=%{&amp;encoding}, \" encoding\r\nendif\r\nset statusline+=%{&amp;fileformat}] \" file format\r\nif filereadable(expand(\"$VIM/vimfiles/plugin/vimbuddy.vim\"))\r\nset statusline+=\\ %{VimBuddy()} \" vim buddy\r\nendif\r\nset statusline+=%= \" right align\r\n\"set statusline+=%2*0x%-8B\\ \" current char\r\nset statusline+=0x%-8B\\ \" current char\r\nset statusline+=%-14.(%l,%c%V%)\\ %&lt;%P \" offset</pre>\r\n<p>3. VIM中设置python自动缩进为4个空格</p>\r\n<p>在vimrc中加入如下代码即可将自动缩进改为4空格</p>\r\n<pre class=\"prettyprint\">autocmd FileType python setlocal et sta sw=4 sts=4</pre>','',1,1,'2012-04-29 13:25:44','2012-04-29 13:25:44',2,'vim, python',NULL),(57,'Ubuntu中的常用命令','ubuntu-usually-command',1,'<ol style=\"margin-left: -20px;\">\r\n<li>删除软件\r\n<pre class=\"prettyprint\">sudo apt-get --purge remove ****</pre>\r\n</li>\r\n</ol>','',1,1,'2012-04-29 15:34:01','2012-04-29 15:34:01',6,'ubuntu',1),(58,'Python中的list.sort与sorted','python-sort-sorted',1,'<p>Python中需要对list进行排序时有两种方法<br />1、用list成员函数sort进行排序<br />2、用built-in函数sorted进行排序（从2.4排序）</p>\r\n<p>下面是以上两个方法在python doc中的说明&nbsp;</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; help(sorted)\r\nHelp on built-in function sorted in module __builtin__:\r\n\r\nsorted(...)\r\n    sorted(iterable, cmp=None, key=None, reverse=False) --&gt; new sorted list\r\n\r\n&gt;&gt;&gt; help(list.sort)\r\nHelp on method_descriptor:\r\n\r\nsort(...)\r\n    L.sort(cmp=None, key=None, reverse=False) -- stable sort *IN PLACE*;\r\n    cmp(x, y) -&gt; -1, 0, 1</pre>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td>iterable</td>\r\n<td>可迭代类型;</td>\r\n</tr>\r\n<tr>\r\n<td>cmp</td>\r\n<td>用于比较的函数，比较什么由key决定,有默认值，迭代集合中的一项;</td>\r\n</tr>\r\n<tr>\r\n<td>key</td>\r\n<td>用列表元素的某个属性和函数进行作为关键字，有默认值，迭代集合中的一项;</td>\r\n</tr>\r\n<tr>\r\n<td>reverse</td>\r\n<td>排序规则. reverse = True（降序） 或者 reverse = False（升序），默认值为False。</td>\r\n</tr>\r\n<tr>\r\n<td>返回值</td>\r\n<td>是一个经过排序的可迭代类型，与iterable一样。</td>\r\n</tr>\r\n</tbody>\r\n</table>\r\n<p>注：一般来说，cmp和key可以使用lambda表达式。sort与sorted的不同在于sort会改变原来的list排序，而sorted会产生一个新的list</p>\r\n<p>sorting basic:</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; print sorted([5, 2, 3, 1, 4])\r\n[1, 2, 3, 4, 5]\r\n&gt;&gt;&gt; L = [5, 2, 3, 1, 4]\r\n&gt;&gt;&gt; L.sort()\r\n&gt;&gt;&gt; print L</pre>\r\n<p>sorting cmp:</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt;L = [(\'b\',2),(\'a\',1),(\'c\',3),(\'d\',4)]\r\n&gt;&gt;&gt;print sorted(L, cmp=lambda x,y:cmp(x[1],y[1]))\r\n[(\'a\', 1), (\'b\', 2), (\'c\', 3), (\'d\', 4)]</pre>\r\n<p>sorting keys:</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt;L = [(\'b\',2),(\'a\',1),(\'c\',3),(\'d\',4)]\r\n&gt;&gt;&gt;print sorted(L, key=lambda x:x[1]))\r\n[(\'a\', 1), (\'b\', 2), (\'c\', 3), (\'d\', 4)]</pre>\r\n<p>sorting reverse:</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; print sorted([5, 2, 3, 1, 4], reverse=True)\r\n[5, 4, 3, 2, 1]\r\n&gt;&gt;&gt; print sorted([5, 2, 3, 1, 4], reverse=False)\r\n[1, 2, 3, 4, 5]</pre>\r\n<p>注：在效率上 key&gt;cmp（key比cmp快）</p>\r\n<p>在sorting keys中我们看到，此时排序过的L是仅仅按照第二个关键字来排序的，如果我们相用第二个关键字排序后再用第一个关键字进行排序呢？</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; L = [(\'d\',2),(\'a\',4),(\'b\',3),(\'c\',2)]\r\n&gt;&gt;&gt; print sorted(L, key=lambda x:(x[1],x[0]))\r\n&gt;&gt;&gt;[(\'c\', 2), (\'d\', 2), (\'b\', 3), (\'a\', 4)]</pre>','',1,1,'2012-05-05 20:12:29','2012-05-05 20:12:29',2,'python, 排序',NULL),(59,'Python中的itemgetter','python-itemgetter',1,'<p>itemgetter()返回一个可调用对象，这个对象从它的运算对象中取回指定的项目</p>\r\n<p>以下是它在python doc中的说明</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; import operator\r\n&gt;&gt;&gt; help(operator.itemgetter)\r\nHelp on class itemgetter in module operator:\r\n\r\nclass itemgetter(__builtin__.object)\r\n |  itemgetter(item, ...) --&gt; itemgetter object\r\n |  \r\n |  Return a callable object that fetches the given item(s) from its operand.\r\n |  After, f=itemgetter(2), the call f(r) returns r[2].\r\n |  After, g=itemgetter(2,5,3), the call g(r) returns (r[2], r[5], r[3])\r\n |  \r\n |  Methods defined here:\r\n |  \r\n |  __call__(...)\r\n |      x.__call__(...) &lt;==&gt; x(...)\r\n |  \r\n |  __getattribute__(...)\r\n |      x.__getattribute__(\'name\') &lt;==&gt; x.name\r\n |  \r\n |  ----------------------------------------------------------------------\r\n |  Data and other attributes defined here:\r\n |  \r\n |  __new__ = &lt;built-in method __new__ of type object&gt;\r\n |      T.__new__(S, ...) -&gt; a new object with type S, a subtype of T</pre>\r\n<p>相当于：</p>\r\n<pre class=\"prettyprint\">def itemgetter(i,*a): \r\n    def func(obj): \r\n        r = obj[i] \r\n        if a: \r\n            r = (r,) + tuple(obj[i] for i in a) \r\n        return r \r\n    return func \r\n\r\n&gt;&gt;&gt; a = [1,2,3] \r\n&gt;&gt;&gt; b=operator.itemgetter(1) \r\n&gt;&gt;&gt; b(a)   ＃ 返回列表 a 中的索引为1的元素\r\n2 \r\n&gt;&gt;&gt; b=operator.itemgetter(1,0) \r\n&gt;&gt;&gt; b(a)   ＃ 返回列表 a 中索引为1和0的元素\r\n(2, 1) \r\n&gt;&gt;&gt; b=itemgetter(1) \r\n&gt;&gt;&gt; b(a) \r\n2 \r\n&gt;&gt;&gt; b=itemgetter(1,0) \r\n&gt;&gt;&gt; b(a) \r\n(2, 1) </pre>','',1,1,'2012-05-05 20:56:45','2012-05-05 20:56:45',2,'python',NULL),(60,'PyQt中QTableView的排序','pyqt-qtableview-sort',1,'<p>PyQt中QTableView只负责显示数据，对数据的管理都在Model中进行，所以要对QTableView中的数据进行排序，就要在Model中实现排序函数，如下是代码：</p>\r\n<pre class=\"prettyprint\"># coding: utf-8\r\nimport sys, os, re, operator\r\nfrom PyQt4 import QtGui, QtCore\r\n\r\ndef main():\r\n    app = QtGui.QApplication(sys.argv)\r\n    w = MyWindow()\r\n    w.show()\r\n    sys.exit(app.exec_())\r\n\r\nclass MyWindow(QtGui.QWidget):\r\n    def __init__(self, *args):\r\n        QtGui.QWidget.__init__(self, *args)        \r\n        # create table\r\n        self.get_table_data()\r\n        table = self.createTable()\r\n        # 设置布局\r\n        layout = QtGui.QVBoxLayout()\r\n        layout.addWidget(table)\r\n        self.setLayout(layout)\r\n\r\n    def get_table_data(self):\r\n        self.tabledata = [[\'2012-05-05\', \'23:35:01\', unicode(\'文本文档\'),\r\n                           \'2.0KB\', \'~/Document/test.txt\'],\r\n                          [\'2012-05-04\', \'23:36:02\', unicode(\'音频文件\'),\r\n                           \'6.5MB\', \'~/Music/sample.mp3\'],\r\n                          [\'2012-05-06\', \'23:37:03\', unicode(\'视频文件\'),\r\n                           \'5.3GB\', \'~/Movie/sample.avi\']]\r\n\r\n    def createTable(self):\r\n        # 创建 Tableview\r\n        tv = QtGui.QTableView()        \r\n        # 创建 table model\r\n        header = [\'date\', \'time\', \'\', \'size\', \'filename\']\r\n        tm = MyTableModel(self.tabledata, header, self)\r\n        tv.setModel(tm)        \r\n        # 设置 tableview 的最小尺寸\r\n        tv.setMinimumSize(400, 300)        \r\n        # 隐藏 tableview 的网格\r\n        tv.setShowGrid(False)\r\n        # 设置字体\r\n        font = QtGui.QFont(\'Courier New\', 10)\r\n        tv.setFont(font)\r\n        # 隐藏垂直方向的表头\r\n        vh = tv.verticalHeader()\r\n        vh.setVisible(False)\r\n        # 设置水平方向的表头（设置最后列的表头是否占用所有剩余空间）\r\n        hh = tv.horizontalHeader()\r\n        hh.setStretchLastSection(True)\r\n        # set column width to fit contents\r\n        tv.resizeColumnsToContents()\r\n        # 设置行高\r\n        nrows = len(self.tabledata)\r\n        for row in xrange(nrows):\r\n            # 把第 row 行的高度设置为18\r\n            tv.setRowHeight(row, 18)\r\n        # 允许排序\r\n        tv.setSortingEnabled(True)\r\n        \r\n        return tv\r\n\r\nclass MyTableModel(QtCore.QAbstractTableModel):\r\n    def __init__(self, datain, headerdata, parent=None, *args):\r\n        \"\"\" datain: a list of lists\r\n            headerdata: a list of strings\r\n        \"\"\"\r\n        QtCore.QAbstractTableModel.__init__(self, parent, *args)\r\n        self.arraydata = datain\r\n        self.headerdata = headerdata\r\n    \r\n    def rowCount(self, parent):\r\n        return len(self.arraydata)\r\n    \r\n    def columnCount(self, parent):\r\n        return len(self.arraydata[0])\r\n    \r\n    def data(self, index, role):\r\n        if not index.isValid():\r\n            return QtCore.QVariant()\r\n        elif role != QtCore.Qt.DisplayRole:\r\n            return QtCore.QVariant()\r\n        else:\r\n            return QtCore.QVariant(self.arraydata[index.row()][index.column()])\r\n    \r\n    def headerData(self, section, orientation, role):\r\n        if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:\r\n            return QtCore.QVariant(self.headerdata[section])\r\n        else:\r\n            return QtCore.QVariant()\r\n    \r\n    def sort(self, col, order):\r\n        \"\"\"Sort table by given column number.\r\n        \"\"\"\r\n        self.emit(QtCore.SIGNAL(\'layoutAboutToBeChanged()\'))\r\n        self.arraydata = sorted(self.arraydata, key=operator.itemgetter(col))\r\n        if order == QtCore.Qt.DescendingOrder:\r\n            self.arraydata.reverse()\r\n        self.emit(QtCore.SIGNAL(\'layoutChanged()\'))\r\n\r\nif __name__ == \"__main__\":\r\n    main()\r\n</pre>','',1,1,'2012-05-05 23:47:25','2012-05-05 23:47:25',19,'python, pyqt, 排序',2),(61,'aria2下载命令','linux-aria2-download',1,'<p>aria2下载工具特点</p>\r\n<ol>\r\n<li>支持http/https/ftp/bt协议</li>\r\n<li>支持metalink3.0，metalink是一种可以将不同协议下载的同一文件集合到一起以达到最大下载稳定性和速度的东西。个人感觉这个似乎比迅雷支持不同网址下载同一文件似乎更高级点</li>\r\n<li>支持分段下载和续传</li>\r\n<li>支持通过http代理的ftp下载</li>\r\n<li>可以作为守候进程运行</li>\r\n</ol>\r\n<p>aria有gtk命令，但aria2只有命令行版本，ubuntu下安装方法为：</p>\r\n<pre class=\"prettyprint\">sudo apt-get install aria2</pre>\r\n<p>常用命令如下：</p>\r\n<pre class=\"prettyprint\">/* 普通下载 */\r\naria2c http://AAA.BBB.CCC/file.zip\r\n/* 开2个线程下载 */\r\naria2c -s 2 http://AAA.BBB.CCC/file.zip\r\n/* 从不同的地址下载同一文件 */\r\naria2c http://AAA.BBB.CCC/file.zip ftp://DDD.EEE.FFF/GGG/file.zip\r\n/* 支持不同的协议下载同一文件 */\r\naria2c http://AAA.BBB.CCC/file.zip ftp://DDD.EEE.FFF/GGG/file.zip\r\n/* 下载BT种子 */\r\naria2c -otest.torrent http://AAA.BBB.CCC/file.torrent\r\n/* 设定BT最大上传速度 */\r\naria2c --max-upload-limit 40K -Tfile.torrent\r\n/* 从metalink下载文件 */\r\naria2c http://AAA.BBB.CCC/file.metalink</pre>\r\n<p>注：https下载依赖于安装gnutls或openssl<br />&nbsp; &nbsp;bt下载依赖于安装gnutls+libgcrypt或openssl<br />&nbsp; &nbsp;metalink下载依赖于安装libxml2</p>\r\n<p>官方地址 http://aria2.sourceforge.net/&nbsp;</p>','',1,1,'2012-05-06 12:29:44','2012-05-06 12:29:44',6,'linux',NULL),(62,'PyQt中QTreeWidget的使用','pyqt-qtreewidget',1,'<p>首先要讲一下QT中item、model、view的关系。item是一条数据项，类似表格中的一行，model是数据模型，类似一张表，view则是将这个model这张显示出来的外在表现。</p>\r\n<p>在以下实例中，QTreeWidget就是一个view，QTreeWidgetItem是一个item，但以下实例并没有使用到model，它是直接把item添加到view中的</p>\r\n<pre class=\"prettyprint\"># coding: utf-8\r\nimport sys\r\nfrom PyQt4 import QtGui\r\n\r\nclass MyWindow(QtGui.QMainWindow):\r\n    def __init__(self, parent=None):\r\n        QtGui.QMainWindow.__init__(self, parent)\r\n        \r\n        self.setWindowTitle(\'TreeWidget\')\r\n        tree = self.createTree()\r\n        self.setCentralWidget(tree)\r\n                \r\n    def createTree(self):\r\n        tree = QtGui.QTreeWidget()\r\n        tree.setColumnCount(2)                  # 共有两列\r\n        tree.setHeaderLabels([\'key\', \'Value\'])  # 设置表头\r\n        root = QtGui.QTreeWidgetItem(tree)      # 设置根节点\r\n        root.setText(0, \'root\')                 # 第一列的文本设为 \'root\'\r\n        child1 = QtGui.QTreeWidgetItem(root)\r\n        child1.setText(0, \'child1\')\r\n        child1.setText(1, \'name\')\r\n        child2 = QtGui.QTreeWidgetItem(root)\r\n        child2.setText(0, \'child2\')\r\n        child2.setText(1, \'name2\')\r\n        child3 = QtGui.QTreeWidgetItem(root)\r\n        child3.setText(0, \'child3\')\r\n        child4 = QtGui.QTreeWidgetItem(child3)\r\n        child4.setText(0, \'child4\')\r\n        child4.setText(1, \'name4\')\r\n        tree.addTopLevelItem(root)              # 添加根节点到tree中\r\n        return tree\r\n\r\ndef main(args):\r\n    app = QtGui.QApplication(args)\r\n    table = MyWindow()\r\n    table.show()\r\n    sys.exit(app.exec_())\r\n    \r\nif __name__ == \"__main__\":\r\n    main(sys.argv)\r\n</pre>\r\n<p>在应用程序中一般不是这样来创建树型结构的，特别是比较复杂的Tree，一般都是通过QTreeView来实现而不是QTreeWidget来实现。这时需要定制model，通过继承QAbstractItemModel来实现。系统也会提供一些已经定制好的model，例如</p>\r\n<pre class=\"prettyprint\"># coding: utf-8\r\nimport sys\r\nfrom PyQt4 import QtGui\r\n\r\nclass MyWindow(QtGui.QMainWindow):\r\n    def __init__(self, parent=None):\r\n        QtGui.QMainWindow.__init__(self, parent)\r\n        \r\n        self.setWindowTitle(\'Dir View\')\r\n        # model = QtGui.QFileSystemModel()\r\n        # model.setRootPath(\'/\')\r\n        model = QtGui.QDirModel()\r\n        tree = QtGui.QTreeView()\r\n        tree.setModel(model)\r\n        self.setCentralWidget(tree)\r\n\r\ndef main(args):\r\n    app = QtGui.QApplication(args)\r\n    table = MyWindow()\r\n    table.show()\r\n    sys.exit(app.exec_())\r\n    \r\nif __name__ == \"__main__\":\r\n    main(sys.argv)</pre>\r\n<p>更多关于QDirModel的信息请参考：http://qt-project.org/doc/qt-4.8/qdirmodel.html</p>','',1,1,'2012-05-06 16:45:01','2012-05-06 16:45:01',19,'python, pyqt',2),(63,'PyQt中QTreeView的使用','pyqt-qtreeview',1,'<p>1、以下实例QStandardItemModel作为model，并实现drap和drop操作</p>\r\n<p>QStandardItemModel提供了一种通用的model用来存储数据，它实现了QAbstractItemModel接口，这意味着它能为所有支持该接口的view提供数据（如，QListView, QTableView和QTreeView）。</p>\r\n<p>当你想构建一个list或tree，你可以先创建一个空的QStandardItemModel实例，并通过appendRow()来添加item到model中，可以使用item()来访问其中的item</p>\r\n<p>如果你想构建一个table，你可以通过一个table的大小来构造一个QStandardItemModel，并使用setItem()来设计table中的数据项。你也可以使用setRowCount()和setColumnCount()去修改model的大小。如果插入数据项，可以使用insertRow()和insertColumn()，如果要删除数据项可以使用removeRow()和removeColumn()</p>\r\n<p>可以使用setHorizontalHeaderLabels()和setVerticalHeaderLabels()来设置表头。</p>\r\n<pre class=\"prettyprint\">import sys\r\nfrom PyQt4 import QtGui, QtCore\r\n\r\nclass MainForm(QtGui.QMainWindow):\r\n    def __init__(self, parent=None):\r\n        super(MainForm, self).__init__(parent)\r\n\r\n        self.model = QtGui.QStandardItemModel()\r\n\r\n        for k in range(0, 4):\r\n            parentItem = self.model.invisibleRootItem()\r\n            for i in range(0, 4):\r\n                item = QtGui.QStandardItem(QtCore.QString(\"item %0 %1\").arg(k).arg(i))\r\n                parentItem.appendRow(item)\r\n                parentItem = item\r\n\r\n        self.view = QtGui.QTreeView()\r\n        self.view.setModel(self.model)\r\n        self.view.setDragDropMode(QtGui.QAbstractItemView.InternalMove)\r\n\r\n        self.setCentralWidget(self.view)\r\n\r\ndef main():\r\n    app = QtGui.QApplication(sys.argv)\r\n    form = MainForm()\r\n    form.show()\r\n    app.exec_()\r\n\r\nif __name__ == \'__main__\':\r\n    main()</pre>\r\n<p>更多QStandardItemModel信息请参考：<a href=\"http://qt-project.org/doc/qt-4.8/qstandarditemmodel.html\">http://qt-project.org/doc/qt-4.8/qstandarditemmodel.html</a></p>\r\n<p>2、使用自定义model</p>\r\n<p>对于自己继承的QAbstractItemModel时需要实现index()方法，该方法通过指定的row, column以及父index返回model中的某个item的index</p>\r\n<pre class=\"prettyprint\"># coding: utf-8\r\nimport sys\r\nfrom PyQt4 import QtGui, QtCore\r\n\r\nclass TreeModel(QtCore.QAbstractItemModel):\r\n    def __init__(self):\r\n        QtCore.QAbstractItemModel.__init__(self)\r\n        self.nodes = [\'node0\', \'node1\', \'node2\']\r\n    \r\n    def index(self, row, column, parent):\r\n        \'\'\' 返回 model 中给定row, column和父项的 item index \'\'\'\r\n        return self.createIndex(row, column, self.nodes[row])\r\n    def parent(self, index):\r\n        \'\'\' 返回给定index的父项，如果不存在父项则返回空的 QModelIndex 对象 \'\'\'\r\n        return QtCore.QModelIndex()\r\n    def rowCount(self, index):\r\n        # internalPointer指向index位置的数据\r\n        if index.internalPointer() in self.nodes:\r\n            return 0\r\n        else:\r\n            return len(self.nodes)\r\n    def columnCount(self, index):\r\n        return 1\r\n    def data(self, index, role):\r\n        if role == 0:\r\n            return index.internalPointer()\r\n        else:\r\n            return None\r\n    def supportedDropActions(self): \r\n        return QtCore.Qt.CopyAction | QtCore.Qt.MoveAction\r\n    def flags(self, index):\r\n        if not index.isValid():\r\n            return QtCore.Qt.ItemIsEnabled\r\n        return QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | \\\r\n               QtCore.Qt.ItemIsDragEnabled | QtCore.Qt.ItemIsDropEnabled \r\n    def mimeTypes(self):\r\n        return [\'text/xml\']\r\n    def mimeData(self, indexes):\r\n        mimedata = QtCore.QMimeData()\r\n        mimedata.setData(\'text/xml\', \'mimeData\')\r\n        return mimedata\r\n    def dropMimeData(self, data, action, row, column, parent):\r\n        print \'dropMimeData %s %s %s %s\' % (data.data(\'text/xml\'), action, row, parent) \r\n        return True\r\n\r\nclass MainForm(QtGui.QMainWindow):\r\n    def __init__(self, parent=None):\r\n        super(MainForm, self).__init__(parent)\r\n\r\n        self.treeModel = TreeModel()\r\n\r\n        self.view = QtGui.QTreeView()\r\n        self.view.setModel(self.treeModel)\r\n        self.view.setDragDropMode(QtGui.QAbstractItemView.InternalMove)\r\n\r\n        self.setCentralWidget(self.view)\r\n\r\ndef main():\r\n    app = QtGui.QApplication(sys.argv)\r\n    form = MainForm()\r\n    form.show()\r\n    app.exec_()\r\n\r\nif __name__ == \'__main__\':\r\n    main()</pre>','',1,1,'2012-05-06 17:35:46','2012-05-06 17:35:46',19,'pyqt, python',2),(64,'如何彻底卸载MySQL','uninstall-mysql',1,'<p>最近想把MySQL升级到5.5，在删除了之前的版本后重新安装新版本时居然提示以前的版本没有安装完成，本次安装无法继续，于是打开Windows服务，发现\"MySQL\"服务仍然存在，并且还是在启动状态，于是就想删除该服务，可是服务是没有单独的卸载程序的，但是Windows下有一个命令是专门用来卸载服务的，需要把开命令行，并输入如下命令（如果是Win7用户，则需要用管理员权限打开）</p>\r\n<pre class=\"prettyprint\">sc delete mysql</pre>\r\n<p>sc.exe是专用用来卸载Windows服务的，如果服务名中间有空格，就需要在服务名前后加上双引号</p>\r\n<p>可是在卸载完服务后，仍然不能进行安装，此时需要清理注册表，用命令行打开：regedit，进行如下操作</p>\r\n<p>HKEY_LOCAL_MACHINE\\SYSTEM\\ControlSet001\\Services\\Eventlog\\Application\\MySQL 目录删除; HKEY_LOCAL_MACHINE\\SYSTEM\\ControlSet002\\Services\\Eventlog\\Application\\MySQL 目录删除; HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\MySQL 目录删除;</p>\r\n<p>然后就可以进行安装了</p>','',1,1,'2012-05-13 16:30:13','2012-05-13 16:30:13',13,'mysql',1),(65,'Python中的线程','python-thread',1,'<p>Python中使用线程的话有两种方式，一种是函数式，另一种是用类来包装的线程对象</p>\r\n<p>1、用thread模块中的start_new_thread函数来产生新的线程</p>\r\n<pre class=\"prettyprint\">### thread_example.py\r\nimport time\r\nimport thread\r\ndef timer(no,interval):  #自己写的线程函数\r\n    while True:\r\n        print \'Thread :(%d) Time:%s\'%(no,time.ctime())\r\n        time.sleep(interval)\r\ndef test():              #使用thread.start_new_thread()来产生2个新的线程\r\n    thread.start_new_thread(timer,(1,1))\r\n    thread.start_new_thread(timer,(2,3))\r\n\r\nif __name__==\'__main__\':\r\n    test()</pre>\r\n<p>这个是thread.start_new_thread(function,args[,kwargs])函数原型，其中function参数是你将要调用的线程函数；args是讲传递给你的线程函数的参数，他必须是个tuple类型；而kwargs是可选的参数。</p>\r\n<p>线程的结束一般依靠线程函数的自然结束；也可以在线程函数中调用thread.exit()，他抛出SystemExit exception，达到退出线程的目的。</p>\r\n<p>2、通过调用threading模块继承threading.Thread类来包装一个线程对象。</p>\r\n<pre class=\"prettyprint\">import threading  \r\nimport time  \r\nclass timer(threading.Thread):     #我的timer类继承自threading.Thread类   \r\n    def __init__(self,no,interval):    \r\n        #在我重写__init__方法的时候要记得调用基类的__init__方法   \r\n        threading.Thread.__init__(self)        \r\n        self.no=no   \r\n        self.interval=interval   \r\n\r\n    def run(self):  #重写run()方法，把自己的线程函数的代码放到这里   \r\n        while True:   \r\n            print \'Thread Object (%d), Time:%s\'%(self.no,time.ctime())   \r\n            time.sleep(self.interval)   \r\n\r\ndef test():   \r\n    threadone=timer(1,1)    #产生2个线程对象   \r\n    threadtwo=timer(2,3)   \r\n    threadone.start()       #通过调用线程对象的.start()方法来激活线程   \r\n    threadtwo.start()   \r\n\r\nif __name__==\'__main__\':   \r\n    test()  </pre>\r\n<p>其实thread和threading的模块中还包含了其他的很多关于多线程编程的东西，例如锁、定时器、获得激活线程列表等等，请大家仔细参考python的文档！</p>','',1,1,'2012-05-13 17:12:53','2012-05-13 17:12:53',2,'python, thread',1),(66,'PyQt自定义信号','pyqt-signal-definition',1,'<p>以下介绍PyQt中的自定义信息以及如何传递参数</p>\r\n<pre class=\"prettyprint\">import sys \r\nimport time \r\nfrom PyQt4.QtCore import * \r\nfrom PyQt4.QtGui import *\r\n\r\nclass MyWindow(QWidget): \r\n    def __init__(self, *args): \r\n        QWidget.__init__(self, *args)\r\n\r\n        self.label = QLabel(\" \") \r\n        layout = QVBoxLayout() \r\n        layout.addWidget(self.label) \r\n        self.setLayout(layout) \r\n        self.connect(self, SIGNAL(\"didSomething\"),\r\n                     self.update_label) \r\n        self.do_something()\r\n\r\n    def do_something(self): \r\n        self.emit(SIGNAL(\"didSomething\"), \"important\", \"information\")\r\n\r\n    def update_label(self, value1, value2): \r\n        self.label.setText(value1 + \" \" + value2)\r\n\r\nif __name__ == \"__main__\": \r\n    app = QApplication(sys.argv) \r\n    w = MyWindow() \r\n    w.show() \r\n    sys.exit(app.exec_())</pre>\r\n<p>也可以按如下方式传递参数</p>\r\n<pre class=\"prettyprint\">import sys \r\nimport time \r\nfrom PyQt4.QtCore import * \r\nfrom PyQt4.QtGui import *\r\n\r\nclass MyWindow(QWidget): \r\n    def __init__(self, *args): \r\n        QWidget.__init__(self, *args)\r\n\r\n        self.label = QLabel(\" \") \r\n        layout = QVBoxLayout() \r\n        layout.addWidget(self.label) \r\n        self.setLayout(layout) \r\n        self.connect(self, SIGNAL(\"didSomething\"),\r\n                     self.update_label) \r\n        self.do_something()\r\n\r\n    def do_something(self): \r\n        self.emit(SIGNAL(\"didSomething\"), \"important\", \"information\")\r\n\r\n    def update_label(self, *args): \r\n        label_str = \'\' \r\n        for argv in args: \r\n            label_str += \' \' + argv \r\n        self.label.setText(label_str + \"asjdfkjadsfkajdfkd\")\r\n\r\nif __name__ == \"__main__\": \r\n    app = QApplication(sys.argv) \r\n    w = MyWindow() \r\n    w.show() \r\n    sys.exit(app.exec_())</pre>','',1,1,'2012-05-13 19:09:39','2012-05-13 19:09:39',19,'pyqt, python',NULL),(67,'Python获取当前系统时间','python',1,'<p>取得时间相关的信息的话，要用到python time模块,python time模块里面有很多非常好用的功能，你可以去官方文档了解下，要取的当前时间的话，要取得当前时间的时间戳，时间戳好像是1970年到现在时间相隔的时间。</p>\r\n<p>你可以试下下面的方式来取得当前时间的时间戳：</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; import time\r\n&gt;&gt;&gt; print time.time()\r\n1336919189.26</pre>\r\n<p>但是这样是一连串的数字不是我们想要的结果，我们可以利用time模块的格式化时间的方法来处理：</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; time.localtime(time.time())\r\n(2012, 5, 13, 22, 27, 32, 6, 134, 0)</pre>\r\n<p>现在看起来更有希望格式成我们想要的时间了。最后用time.strftime()方法，把刚才的一大串信息格式化成我们想要的东西，现在的结果是：</p>\r\n<pre class=\"prettyprint\">&gt;&gt;&gt; time.strftime(\'%Y-%m-%d\',time.localtime(time.time()))\r\n\'2012-05-13\'</pre>\r\n<p>time.strftime里面有很多参数，可以让你能够更随意的输出自己想要的东西：</p>\r\n<p>下面是time.strftime的参数：<br /> strftime(format[, tuple]) -&gt; string<br /> 将指定的struct_time(默认为当前时间)，根据指定的格式化字符串输出</p>\r\n<p>python中时间日期格式化符号：</p>\r\n<table>\r\n<tbody>\r\n<tr>\r\n<td>%y</td>\r\n<td>两位数的年份表示（00-99）</td>\r\n</tr>\r\n<tr>\r\n<td>%Y</td>\r\n<td>四位数的年份表示（000-9999）</td>\r\n</tr>\r\n<tr>\r\n<td>%m</td>\r\n<td>月份（01-12）</td>\r\n</tr>\r\n<tr>\r\n<td>%d</td>\r\n<td>月内中的一天（0-31）</td>\r\n</tr>\r\n<tr>\r\n<td>%H</td>\r\n<td>24小时制小时数（0-23）</td>\r\n</tr>\r\n<tr>\r\n<td>%I</td>\r\n<td>12小时制小时数（01-12）</td>\r\n</tr>\r\n<tr>\r\n<td>%M</td>\r\n<td>分钟数（00=59）</td>\r\n</tr>\r\n<tr>\r\n<td>%S</td>\r\n<td>秒（00-59）</td>\r\n</tr>\r\n<tr>\r\n<td>&nbsp;</td>\r\n<td>&nbsp;</td>\r\n</tr>\r\n<tr>\r\n<td>%a</td>\r\n<td>本地简化星期名称</td>\r\n</tr>\r\n<tr>\r\n<td>%A</td>\r\n<td>本地完整星期名称</td>\r\n</tr>\r\n<tr>\r\n<td>%b</td>\r\n<td>本地简化的月份名称</td>\r\n</tr>\r\n<tr>\r\n<td>%B</td>\r\n<td>本地完整的月份名称</td>\r\n</tr>\r\n<tr>\r\n<td>%c</td>\r\n<td>本地相应的日期表示和时间表示</td>\r\n</tr>\r\n<tr>\r\n<td>%j</td>\r\n<td>年内的一天（001-366）</td>\r\n</tr>\r\n<tr>\r\n<td>%p</td>\r\n<td>本地A.M.或P.M.的等价符</td>\r\n</tr>\r\n<tr>\r\n<td>%U</td>\r\n<td>一年中的星期数（00-53）星期天为星期的开始</td>\r\n</tr>\r\n<tr>\r\n<td>%w</td>\r\n<td>星期（0-6），星期天为星期的开始</td>\r\n</tr>\r\n<tr>\r\n<td>%W</td>\r\n<td>一年中的星期数（00-53）星期一为星期的开始</td>\r\n</tr>\r\n<tr>\r\n<td>%x</td>\r\n<td>本地相应的日期表示</td>\r\n</tr>\r\n<tr>\r\n<td>%X</td>\r\n<td>本地相应的时间表示</td>\r\n</tr>\r\n<tr>\r\n<td>%Z</td>\r\n<td>当前时区的名称</td>\r\n</tr>\r\n<tr>\r\n<td>%%</td>\r\n<td>%号本身</td>\r\n</tr>\r\n</tbody>\r\n</table>','',1,1,'2012-05-13 22:33:44','2012-05-13 22:33:44',2,'python',NULL),(68,'PyQt利用QPainter绘图的坐标系转换  ','pyqt-qpainter-coordinate-translate',1,'<p>这里先说一下Qt的坐标系，然后讲解那几个函数，它们分别是：</p>\r\n<ul>\r\n<li>translate()函数，进行平移变换</li>\r\n<li>scale()函数，进行比例变换</li>\r\n<li>rotate()函数，进行旋转变换</li>\r\n<li>shear()函数，进行扭曲变换</li>\r\n</ul>\r\n<p>最后介绍两个有用的函数save()和restore()，利用它们来保存和弹出坐标系的状态，从而实现快速利用几个变换来绘图。</p>\r\n<p>一、坐标系简介。</p>\r\n<p>Qt中每一个窗口都有一个坐标系，默认的，窗口左上角为坐标原点，然后水平向右依次增大，水平向左依次减小，垂直向下依次增大，垂直向上依次减小。原点即为（0，0）点，然后以像素为单位增减。</p>\r\n<pre class=\"prettyprint\">from PyQt4.QtGui import *\r\nfrom PyQt4.QtCore import *\r\n\r\nclass MyWindow(QWidget):\r\n    def __init__(self, parent=None):\r\n        QWidget.__init__(self, parent)\r\n        self.resize(400, 400)\r\n\r\n    def paintEvent(self, event):\r\n        painter = QPainter(self)\r\n        painter.setBrush(Qt.red)\r\n        painter.drawRect(0, 0, 100, 100)\r\n        painter.setBrush(Qt.yellow)\r\n        painter.drawRect(-50, -50, 100, 100)\r\n\r\nif __name__ == \'__main__\':\r\n    app = QApplication([])\r\n    w = MyWindow()\r\n    w.show()\r\n    app.exec_()</pre>\r\n<p>我们先在原点（0，0）绘制了一个长宽都是100像素的红色矩形，又在（-50，-50）点绘制了一个同样大小的黄色矩形。可以看到，我们只能看到黄色矩形的一部分。</p>\r\n<p>二、坐标系变换。</p>\r\n<p>坐标系变换是利用变换矩阵来进行的，我们可以利用QTransform类来设置变换矩阵，因为一般我们不需要进行更改，所以这里不在涉及。下面我们只是对坐标系的平移，缩放，旋转，扭曲等应用进行介绍。</p>\r\n<p>1.利用translate()函数进行平移变换。</p>\r\n<pre class=\"prettyprint\"># coding: utf-8\r\nfrom PyQt4.QtGui import *\r\nfrom PyQt4.QtCore import *\r\n\r\nclass MyWindow(QWidget):\r\n    def __init__(self, parent=None):\r\n        QWidget.__init__(self, parent)\r\n        self.resize(400, 400)\r\n\r\n    def paintEvent(self, event):\r\n        painter = QPainter(self)\r\n        painter.setBrush(Qt.yellow)\r\n        painter.drawRect(0, 0, 50, 50)\r\n        painter.translate(100, 100) # 将点（100，100）设为原点\r\n        painter.setBrush(Qt.red)\r\n        painter.drawRect(0, 0, 50, 50)\r\n        painter.translate(-100, -100)\r\n        painter.drawLine(0, 0, 20, 20)\r\n\r\nif __name__ == \'__main__\':\r\n    app = QApplication([])\r\n    w = MyWindow()\r\n    w.show()\r\n    app.exec_()</pre>\r\n<p>这里将（100，100）点作为了原点，所以此时（100，100）就是（0，0）点，以前的（0，0）点就是（-100，-100）点。要想使原来的（0，0）点重新成为原点，就是将（-100，-100）设为原点。</p>\r\n<p>2.利用scale()函数进行比例变换，实现缩放效果。</p>\r\n<pre class=\"prettyprint\"># coding: utf-8\r\nfrom PyQt4.QtGui import *\r\nfrom PyQt4.QtCore import *\r\n\r\nclass MyWindow(QWidget):\r\n    def __init__(self, parent=None):\r\n        QWidget.__init__(self, parent)\r\n        self.resize(400, 400)\r\n\r\n    def paintEvent(self, event):\r\n        painter = QPainter(self)\r\n        painter.setBrush(Qt.yellow)\r\n        painter.drawRect(0, 0, 100, 100)\r\n        painter.scale(2, 2) # 放大两倍\r\n        painter.setBrush(Qt.red)\r\n        painter.drawRect(50, 50, 50, 50)\r\n        \r\nif __name__ == \'__main__\':\r\n    app = QApplication([])\r\n    w = MyWindow()\r\n    w.show()\r\n    app.exec_()</pre>\r\n<p>可以看到，painter.scale(2,2)，是将横纵坐标都扩大了两倍，现在的（50，50）点就相当于以前的 （100，100）点。</p>\r\n<p>3.利用shear()函数就行扭曲变换。</p>\r\n<pre class=\"prettyprint\"># coding: utf-8\r\nfrom PyQt4.QtGui import *\r\nfrom PyQt4.QtCore import *\r\n\r\nclass MyWindow(QWidget):\r\n    def __init__(self, parent=None):\r\n        QWidget.__init__(self, parent)\r\n        self.resize(400, 400)\r\n\r\n    def paintEvent(self, event):\r\n        painter = QPainter(self)\r\n        painter.setBrush(Qt.yellow)\r\n        painter.drawRect(0, 0, 50, 50)\r\n        painter.shear(0, 2) # 纵向扭曲变形\r\n        painter.setBrush(Qt.red)\r\n        painter.drawRect(50, 0, 50, 50)\r\n\r\nif __name__ == \'__main__\':\r\n    app = QApplication([])\r\n    w = MyWindow()\r\n    w.show()\r\n    app.exec_()</pre>\r\n<p>这里，painter.shear(0,1)，是对纵向进行扭曲，0表示不扭曲，当将第一个0更改时就会对横行进行扭曲，关于扭曲变换到底是什么效果，你观察一下是很容易发现的。</p>\r\n<p>4.利用rotate()函数进行旋转变换。</p>\r\n<pre class=\"prettyprint\"># coding: utf-8\r\nfrom PyQt4.QtGui import *\r\nfrom PyQt4.QtCore import *\r\n\r\nclass MyWindow(QWidget):\r\n    def __init__(self, parent=None):\r\n        QWidget.__init__(self, parent)\r\n        self.resize(400, 400)\r\n\r\n    def paintEvent(self, event):\r\n        painter = QPainter(self)\r\n        painter.drawLine(0, 0, 100, 0)\r\n        painter.rotate(30)  # 以原点为中心，顺时针旋转30度\r\n        painter.drawLine(0, 0, 100, 0)\r\n        painter.translate(100, 100)\r\n        painter.rotate(30)\r\n        painter.drawLine(0, 0, 100, 0)\r\n\r\nif __name__ == \'__main__\':\r\n    app = QApplication([])\r\n    w = MyWindow()\r\n    w.show()\r\n    app.exec_()</pre>\r\n<p>因为默认的rotate()函数是以原点为中心进行顺时针旋转的，所以我们要想使其以其他点为中心进行旋转，就要先进行原点的变换。这里的painter.translate(100,100)将（100，100）设置为新的原点，想让直线以其为中心进行旋转，可是你已经发现效果并非如此。是什么原因呢？我们添加一条语句，如下：</p>\r\n<pre class=\"prettyprint\"># coding: utf-8\r\nfrom PyQt4.QtGui import *\r\nfrom PyQt4.QtCore import *\r\n\r\nclass MyWindow(QWidget):\r\n    def __init__(self, parent=None):\r\n        QWidget.__init__(self, parent)\r\n        self.resize(400, 400)\r\n\r\n    def paintEvent(self, event):\r\n        painter = QPainter(self)\r\n        painter.drawLine(0, 0, 100, 0)\r\n        painter.rotate(30)  # 以原点为中心，顺时针旋转30度\r\n        painter.drawLine(0, 0, 100, 0)\r\n        painter.rotate(-30)\r\n        painter.translate(100, 100)\r\n        painter.rotate(30)\r\n        painter.drawLine(0, 0, 100, 0)\r\n\r\nif __name__ == \'__main__\':\r\n    app = QApplication([])\r\n    w = MyWindow()\r\n    w.show()\r\n    app.exec_()</pre>\r\n<p>这时就是我们想要的效果了。我们加的一句代码为painter.rotate(-30)，这是因为前面已经将坐标旋转了30度，我们需要将其再旋转回去，才能是以前正常的坐标系统。不光这个函数如此，这里介绍的这几个函数均如此，所以很容易出错。下面我们将利用两个函数来很好的解决这个问题。</p>\r\n<p>三、坐标系状态的保护。</p>\r\n<p>我们可以先利用save()函数来保存坐标系现在的状态，然后进行变换操作，操作完之后，再用restore()函数将以前的坐标系状态恢复，其实就是一个入栈和出栈的操作。</p>\r\n<pre class=\"prettyprint\"># coding: utf-8\r\nfrom PyQt4.QtGui import *\r\nfrom PyQt4.QtCore import *\r\n\r\nclass MyWindow(QWidget):\r\n    def __init__(self, parent=None):\r\n        QWidget.__init__(self, parent)\r\n        self.resize(400, 400)\r\n\r\n    def paintEvent(self, event):\r\n        painter = QPainter(self)\r\n        painter.save()  # 保存坐标系状态\r\n        painter.translate(100, 100)\r\n        painter.drawLine(0, 0, 50, 50)\r\n        painter.restore()   #恢复以前的坐标系状态\r\n        painter.drawLine(0, 0, 50, 50)\r\n\r\nif __name__ == \'__main__\':\r\n    app = QApplication([])\r\n    w = MyWindow()\r\n    w.show()\r\n    app.exec_()</pre>\r\n<p>利用好这两个函数，可以实现快速的坐标系切换，绘制出不同的图形。</p>','',1,1,'2012-05-19 23:48:08','2012-05-19 23:48:08',19,'python, pyqt',1),(69,'QWidget画图--模拟时钟','pyqt-analog-clock',1,'<p>QWidget作为QPaintDevice的子类，可以通过QPainter的实例来绘制其显示的内容。每个Widget都是通过paintEvent()函数来实现绘图，它会在Widget随时需要重新绘制时调用。该示例将展示如何在自定义Widget中绘制内容，该示例同时展示了为使QPainter绘图更加简单的平移和缩放等坐标系统转换。</p>\r\n<p>AnalogClock类实现了一个带有时针和分钟的时钟Widget，它会每隔几秒钟更新一次。我们子类化一个QWidget并重新实现 paintEvent() 函数来绘制clock。</p>\r\n<pre class=\"prettyprint\">class AnalogClock(QtGui.QWidget):\r\n    def __init__(self, parent=None):\r\n        QtGui.QWidget.__init__(self, parent)\r\n        timer = QtCore.QTimer(self)\r\n        self.connect(timer, QtCore.SIGNAL(\'timeout()\'), \r\n                     self, QtCore.SLOT(self.update()))\r\n        timer.start(1000)\r\n\r\n        self.setWindowTitle(\'Analog Clock\')\r\n        self.resize(200, 200)</pre>\r\n<p>在widget初始化时我们设置了一个每隔一秒就会触发一次的计时器，并把它连接到 update() SLOT上，这样每当计时器发出timeout()信号，时钟就会刷新一次。最后我们重新调整widget的大小为一个适合的大小。</p>\r\n<pre class=\"prettyprint\">def paintEvent(self, event):\r\n    self.hourHand = (QtCore.QPoint(7, 8), \r\n                     QtCore.QPoint(-7, 8), \r\n                     QtCore.QPoint(0, -40))\r\n    self.minuteHand = (QtCore.QPoint(7, 8), \r\n                       QtCore.QPoint(-7, 8), \r\n                       QtCore.QPoint(0, -70))\r\n\r\n    hourColor = QtGui.QColor(127, 0, 127)\r\n    minuteColor = QtGui.QColor(0, 127, 127, 191)\r\n    side = min(self.width(), self.height())\r\n    time = datetime.datetime.now()</pre>\r\n<p>paintEvent()函数会在widget内容需要更新是调用。它会在widget初次显示时触发，同时也会在widget的update() SLOT触发时执行。</p>\r\n<p>在我们设置painter和绘制时钟之前，首先定义了QPaint和QColor的各两个元组，用来作为时针和分针。在分针上设置了一个191的Alpha值，意味着不透明度为75%。</p>\r\n<p>同时还需要决定widget的最短的边，这样好放下时钟界面，同时还需要记下当前时间。</p>\r\n<pre class=\"prettyprint\">painter = QtGui.QPainter(self)\r\npainter.setRenderHint(QtGui.QPainter.Antialiasing)\r\npainter.translate(self.width() / 2, self.height() / 2)\r\npainter.scale(side / 200.0, side / 200.0)</pre>\r\n<p>widget的内容通过QPainter来绘制，QPainter可以在任何QPaintDevice上绘制，但它们通过都被用在widget上，所以我们在实例化QPainter时把widget实例(self)作为参数传递给QPainter的构造函数。</p>\r\n<p>我们调用了QPainter.Antialiasing为参数的QPainter.setRenderHint函数来实现抗锯齿效果。这样会使斜线绘制得更加平滑。</p>\r\n<p>translation函数将坐标系平移到widget的正中央，scale将确保所画的图形按比例缩放到适合widget的大小。这样可以使用值为-100到100的x, y坐标来使图形自动延伸到适合widget最短边的大小</p>\r\n<pre class=\"prettyprint\">painter.setPen(QtCore.Qt.NoPen)\r\npainter.setBrush(hourColor)\r\n\r\npainter.save()\r\npainter.rotate(30.0 * (time.hour + time.minute / 60.0))\r\npainter.drawConvexPolygon(self.hourHand[0], \r\n                          self.hourHand[1], \r\n                          self.hourHand[2])\r\npainter.restore()</pre>\r\n<p>绘制时针，根据当前时间来计算出时针的偏移度，以此坐标系来设置坐标系。</p>\r\n<pre class=\"prettyprint\">painter.setPen(hourColor)\r\nfor i in range(12):\r\n    painter.drawLine(88, 0, 96, 0)\r\n    painter.rotate(30.0)</pre>\r\n<p>设置时针的12个刻度，代表12个小时，每个小时的刻度刚好相差30度，所以每次把坐标系旋转30度。</p>\r\n<pre class=\"prettyprint\">painter.setPen(QtCore.Qt.NoPen)\r\npainter.setBrush(minuteColor)\r\npainter.save()\r\npainter.rotate(6.0 * (time.minute + time.second / 60.0))\r\npainter.drawConvexPolygon(self.minuteHand[0], \r\n                          self.minuteHand[1], \r\n                          self.minuteHand[2])\r\npainter.restore()\r\n\r\npainter.setPen(minuteColor)\r\nfor i in range(60):\r\n    if (i % 5) != 0:\r\n        painter.drawLine(92, 0, 96, 0)\r\n    painter.rotate(6.0)</pre>\r\n<p>以上代码以同时的方式来显示分针，并画出60个分钟刻度，但要注意的是由于每隔5分钟会遇到时针的刻度，所以在计数器为5时，不需要画出刻度。</p>\r\n<p>以下是该程序的完整代码：</p>\r\n<pre class=\"prettyprint\"># coding: utf-8\r\nimport datetime\r\nfrom PyQt4 import QtGui, QtCore\r\n\r\nclass AnalogClock(QtGui.QWidget):\r\n    def __init__(self, parent=None):\r\n        QtGui.QWidget.__init__(self, parent)\r\n        timer = QtCore.QTimer(self)\r\n        self.connect(timer, QtCore.SIGNAL(\'timeout()\'), \r\n                     self, QtCore.SLOT(self.update()))\r\n        timer.start(1000)\r\n\r\n        self.setWindowTitle(\'Analog Clock\')\r\n        self.resize(200, 200)\r\n\r\n        \r\n\r\n    def paintEvent(self, event):\r\n        self.hourHand = (QtCore.QPoint(7, 8), \r\n                         QtCore.QPoint(-7, 8), \r\n                         QtCore.QPoint(0, -40))\r\n        self.minuteHand = (QtCore.QPoint(7, 8), \r\n                           QtCore.QPoint(-7, 8), \r\n                           QtCore.QPoint(0, -70))\r\n\r\n        hourColor = QtGui.QColor(127, 0, 127)\r\n        minuteColor = QtGui.QColor(0, 127, 127, 191)\r\n        side = min(self.width(), self.height())\r\n        time = datetime.datetime.now()\r\n\r\n        painter = QtGui.QPainter(self)\r\n        painter.setRenderHint(QtGui.QPainter.Antialiasing)\r\n        painter.translate(self.width() / 2, self.height() / 2)\r\n        painter.scale(side / 200.0, side / 200.0)\r\n        \r\n        painter.setPen(QtCore.Qt.NoPen)\r\n        painter.setBrush(hourColor)\r\n        \r\n        painter.save()\r\n        painter.rotate(30.0 * (time.hour + time.minute / 60.0))\r\n        painter.drawConvexPolygon(self.hourHand[0], \r\n                                  self.hourHand[1], \r\n                                  self.hourHand[2])\r\n        painter.restore()\r\n                \r\n        painter.setPen(hourColor)\r\n        for i in range(12):\r\n            painter.drawLine(88, 0, 96, 0)\r\n            painter.rotate(30.0)\r\n        \r\n        painter.setPen(QtCore.Qt.NoPen)\r\n        painter.setBrush(minuteColor)\r\n        painter.save()\r\n        painter.rotate(6.0 * (time.minute + time.second / 60.0))\r\n        painter.drawConvexPolygon(self.minuteHand[0], \r\n                                  self.minuteHand[1], \r\n                                  self.minuteHand[2])\r\n        painter.restore()\r\n        \r\n        painter.setPen(minuteColor)\r\n        for i in range(60):\r\n            if (i % 5) != 0:\r\n                painter.drawLine(92, 0, 96, 0)\r\n            painter.rotate(6.0)\r\n\r\nif __name__ == \'__main__\':\r\n    app = QtGui.QApplication([])\r\n    w = AnalogClock()\r\n    w.show()\r\n    app.exec_()</pre>','',1,1,'2012-05-20 12:42:14','2012-05-20 12:42:14',19,'python, pyqt',NULL),(70,'eclipse中配置PyQt','pyqt-eclipse-configuration',1,'<p>在python，pyqt，eclipse等一切安装好后，发现在eclipse中写pyqt程序时，pyqt的类库无法识别，但是程序依然能够运行，只是IDE中多了很多错误提示，非常的不爽。下面通过设置eclipse去年那些烦人的错误</p>\r\n<p>1、在Eclipse的Window-&gt;Preference-&gt;PyDev-&gt;Interpreter-Python 下的Libraries添加PyQt安装后在Python文件夹下的路径</p>\r\n<p>2、然后在Forced Builtins选项卡中添加PyQt4中的模块，可以在IDLE中使用import PyQt4 &nbsp; help(PyQt4)查看有哪些模块，分别添加进去。下面是列举好的模块，可以直接复制下来一次性全添加进去：</p>\r\n<p>PyQt4.QAxContainer, PyQt4.Qsci, PyQt4.Qt, PyQt4.QtCore, PyQt4.QtDeclarative, PyQt4.QtDesigner, PyQt4.QtGui, PyQt4.QtHelp, PyQt4.QtMultimedia, PyQt4.QtNetwork, PyQt4.QtOpenGL, PyQt4.QtScript, PyQt4.QtScriptTools, PyQt4.QtSql, PyQt4.QtSvg, PyQt4.QtTest, PyQt4.QtWebKit, PyQt4.QtXml, PyQt4.QtXmlPatterns</p>','',1,1,'2012-05-20 13:25:41','2012-05-20 13:25:41',19,'pyqt',1),(71,'QWidget画图--涂鸦程序','pyqt-scribble-example',1,'<p>该程序演示如何实现QWidget中的事件处理程序来接收widget产生的事件。我们通过mouse事件处理器来实现绘图，paint事件处理器来更新程序界面以及resize事件处理器来优化程序的显示效果。另外，我们重新实现了close事件处理器在关闭程序之前捕获关闭事件。</p>\r\n<p>该事件同时演示了如何使用QPainter来实时画图。</p>\r\n<p>在涂鸦程序中用户可以自行画图。&ldquo;File&rdquo;菜单可以打开和编辑一个已有图片文件，保存图片以及退出程序。在画图时，&ldquo;Options&rdquo;菜单允许用户选择画笔的颜色及宽度，除此之外，还可以清空整个画板。另外&ldquo;Help&rdquo;菜单可以提供一些关于该涂鸦程序的帮助信息，以及关于QT的一些信息。</p>\r\n<p>该示例程序由两大类组成：</p>\r\n<ul>\r\n<li>ScribbleArea 是一个自定义的widget用来显示一个QImage并且允许用户在上面画图</li>\r\n<li>MainWindow 在ScribbleArea上提供一个菜单</li>\r\n</ul>\r\n<p>下面讲解ScribbleArea类，MainWindow类将会以代码的形式，通过注释来说明</p>\r\n<pre class=\"prettyprint\">class ScribbleArea(QtGui.QWidget):\r\n    def __init__(self, parent=None):\r\n        super(ScribbleArea, self).__init__(parent)\r\n\r\n        self.setAttribute(QtCore.Qt.WA_StaticContents)\r\n        self.modified = False\r\n        self.scribbling = False\r\n        self.myPenWidth = 1\r\n        self.myPenColor = QtCore.Qt.blue\r\n        imageSize = QtCore.QSize(500, 500)\r\n        self.image = QtGui.QImage(imageSize, QtGui.QImage.Format_RGB32)\r\n        self.lastPoint = QtCore.QPoint()</pre>\r\n<p>ScribbleArea类继承自QWidget类，在初始化函数中为widget设置了<a href=\"http://qt-project.org/doc/qt-4.8/qt.html#WidgetAttribute-enum\">QtCore.Qt.WA_StaticContents</a>属性，指示widget中的内容会被定位在左上角，不会随着窗口大小的改变而改变。Qt使用此属性在窗口大小改变时来优化paint事件。<br />modified为true表示图像未被保存<br />scribbling为true表示表示用户已经在绘图区域按下了鼠标左键<br />myPenWidth和 myPenColor设置画笔的宽度和颜色<br />image用来表示用户绘制和图像<br />lastPoint保存用户在最后一次按下鼠标键或鼠标移动时的最后位置</p>\r\n<pre class=\"prettyprint\">def openImage(self, fileName):\r\n    loadedImage = QtGui.QImage()\r\n    if not loadedImage.load(fileName):\r\n        return False\r\n\r\n    w = loadedImage.width()\r\n    h = loadedImage.height()    \r\n    self.mainWindow.resize(w, h)\r\n\r\n    self.image = loadedImage\r\n    self.modified = False\r\n    self.update()\r\n    return True</pre>\r\n<p>在openImage函数中会加载选中的图像，获取<a href=\"http://qt-project.org/doc/qt-4.8/qimage.html\">QImage</a>图像的长度和宽度，调整widget的大小来适合图像的大小。然后把加载后的图像保存到成员变量中，最后使用<a href=\"http://qt-project.org/doc/qt-4.8/qwidget.html#update\">QWidget.update</a>函数来产生一个repaint。</p>\r\n<pre class=\"prettyprint\">def saveImage(self, fileName, fileFormat):\r\n    visibleImage = self.image\r\n    self.resizeImage(visibleImage, self.size())\r\n\r\n    if visibleImage.save(fileName, fileFormat):\r\n        self.modified = False\r\n        return True\r\n    else:\r\n        return False</pre>\r\n<p>saveImage函数创建了一个<a href=\"http://qt-project.org/doc/qt-4.8/qimage.html\">QImage</a>对象，并使用<a href=\"http://qt-project.org/doc/qt-4.8/qimage.html#save\">QImage.save</a>函数保存。如果保存成功，我们会把modified设置为False，因为已经不存在保存的数据。</p>\r\n<pre class=\"prettyprint\">def setPenColor(self, newColor):\r\n    self.myPenColor = newColor\r\n\r\ndef setPenWidth(self, newWidth):\r\n    self.myPenWidth = newWidth</pre>\r\n<p>setPenColor() 和setPenWidth 函数来设置当前画笔的颜色和宽度。这些值会被用到将来的画图操作中</p>\r\n<pre class=\"prettyprint\">def clearImage(self):\r\n    self.image.fill(QtGui.qRgb(255, 255, 255))\r\n    self.modified = True\r\n    self.update()</pre>\r\n<p>clearImage函数用来清除画面区域的图像。通过RGB色(255,255,255)，简单的把整个区域置为空白。通常，如果修改了图片就会把modified置为True，并且产生一个repaint。</p>\r\n<pre class=\"prettyprint\">def mousePressEvent(self, event):\r\n    if event.button() == QtCore.Qt.LeftButton:\r\n        self.lastPoint = event.pos()\r\n        self.scribbling = True\r\n\r\ndef mouseMoveEvent(self, event):\r\n    if (event.buttons() &amp; QtCore.Qt.LeftButton) and self.scribbling:\r\n        self.drawLineTo(event.pos())\r\n\r\ndef mouseReleaseEvent(self, event):\r\n    if event.button() == QtCore.Qt.LeftButton and self.scribbling:\r\n        self.drawLineTo(event.pos())\r\n        self.scribbling = False</pre>\r\n<p>在鼠标按下和释放事件中，我们使用<a href=\"http://qt-project.org/doc/qt-4.8/qmouseevent.html#button\">QMouseEvent.button()</a>函数去找到哪个键产生了该事件，而在鼠标移动事件中，我们使用<a href=\"http://qt-project.org/doc/qt-4.8/qmouseevent.html#buttons\">QMouseEvent.buttons()</a>去找到哪个键正在被按住。</p>\r\n<p>如果用户按下了鼠标左键，我们会把按下时的光标位置保存在 lastPoint 中，同时把 scribbling 设为True 表示用户当前正在画图。（scribbling变量是必须的，因为我们无法知道在widget中鼠标的移动总在持续进行。）</p>\r\n<p>如果用户按下了鼠标左键并且进行了移动或者释放了鼠标左键，我们将会调用drawLineTo函数进行绘图。</p>\r\n<pre class=\"prettyprint\">def paintEvent(self, event):\r\n    painter = QtGui.QPainter(self)\r\n    painter.drawImage(event.rect(), self.image)</pre>\r\n<p>在重新实现的 paintEvent() 函数中，我们简单的为绘图区域创建了一个 QPainter，然后画图</p>\r\n<p>在这里，你可能想知道为什么我们不直接在 widget 上画图，而是先在一个 QImage 上进行画图，然后再在 paintEvent() 函数里把 QImage 拷贝到屏幕上，至少有以下三点原因：</p>\r\n<ul>\r\n<li>Windows 系统需要我们随时来重新画图。例如，如果对窗口进行了最小化然后再恢复窗口大小时，Windows 系统可能会忘记原来 widget 中的内容。换句说话，我们不能依靠 Windows 系统去记住原来的图片</li>\r\n<li>Qt 通常不允许我们在 paintEvent() 之外的地方画图。特别地，我们不能在鼠标的事件处理程序中画图。（尽管这可以通过 Qt.WA_PaintOnScreen 属性改变这一点。）</li>\r\n<li>在初始化时，一个 QImage 会有一个 8 位颜色通道（red, green, blue and alpha），然而一个 QWidget 可能会有一个更低的颜色值（这取决于屏幕的配置）。这意味着如果我们加载一个 24 位或 32 位的图片并且把它画到 QWidget 上之后，再从 QWidget 拷贝到 QImage 上，我们会发现会有些失真。</li>\r\n</ul>\r\n<pre class=\"prettyprint\">def resizeEvent(self, event):\r\n    self.resizeImage(self.image, event.size())\r\n    super(ScribbleArea, self).resizeEvent(event)</pre>\r\n<p>如果用户启动了该程序，就会产生一个 resize 事件并且创建一个图片和显示到绘图区域。</p>\r\n<pre class=\"prettyprint\">def drawLineTo(self, endPoint):\r\n    painter = QtGui.QPainter(self.image)\r\n    painter.setPen(QtGui.QPen(self.myPenColor,\r\n                              self.myPenWidth,\r\n                              QtCore.Qt.SolidLine,\r\n                              QtCore.Qt.RoundCap,\r\n                              QtCore.Qt.RoundJoin))\r\n    painter.drawLine(self.lastPoint, endPoint)\r\n    self.modified = True\r\n\r\n    self.update()\r\n    self.lastPoint = QtCore.QPoint(endPoint)</pre>\r\n<p>在 drawLineTo()，我们画一些从 pastPoint 到当前鼠标位置的直线，并且把 modified 设为True，然后产生一个 repaint 事件，并且更新 lastPoint 用来在下次调用 drawLineTo() 时使用。</p>\r\n<pre class=\"prettyprint\">def resizeImage(self, image, newSize):\r\n    if image.size() == newSize:\r\n        return\r\n    \r\n    newImage = QtGui.QImage(newSize, QtGui.QImage.Format_RGB32)\r\n    newImage.fill(QtGui.qRgb(255, 255, 255))\r\n    painter = QtGui.QPainter(newImage)\r\n    painter.drawImage(QtCore.QPoint(0, 0), image)\r\n\r\n    self.image = newImage</pre>\r\n<p>QImage 没有合适的 API 来调整图片的大小。有一个 QImage.copy() 函数可以实现，但是在拉伸时会在新的区域填充为黑色，尽管我们想要的是白色。</p>\r\n<p>为了调整大小，有个诀窍就是用新的大小来创建一个新的 QImage 并且全部填充为白色，再通过 QPainter 来把旧的图片画到上面。新的图片使用 QImage.Format_RGB32 格式，意味着每个像素会保存为 0xffRRGGBB（RR、GG 和BB分别是红、绿、蓝，ff 是 16 进制的 255）。</p>\r\n<pre class=\"prettyprint\">def print_(self):\r\n    printer = QtGui.QPrinter(QtGui.QPrinter.HighResolution)\r\n\r\n    printDialog = QtGui.QPrintDialog(printer, self)\r\n    if printDialog.exec_() == QtGui.QDialog.Accepted:\r\n        painter = QtGui.QPainter(printer)\r\n        rect = painter.viewport()\r\n        size = self.image.size()\r\n        size.scale(rect.size(), QtCore.Qt.KeepAspectRatio)\r\n        painter.setViewport(rect.x(), rect.y(), size.width(), size.height())\r\n        painter.setWindow(self.image.rect())\r\n        painter.drawImage(0, 0, self.image)\r\n        painter.end()</pre>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 2246px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">我们创建一个高保真的 QPrinter 对象，使用 QPrintDialog 记用户指定纸张大小及打印格式。</div>\r\n<div id=\"_mcePaste\" style=\"position: absolute; left: -10000px; top: 2246px; width: 1px; height: 1px; overflow-x: hidden; overflow-y: hidden;\">如果对话框被接收，将会执行打印任务。</div>\r\n<p>我们创建一个高保真的 QPrinter 对象，使用 QPrintDialog 记用户指定纸张大小及打印格式。</p>\r\n<p>如果对话框被接收，将会执行打印任务。</p>\r\n<p>以上程序的完整代码：</p>\r\n<pre class=\"prettyprint\"># coding: utf-8\r\nfrom PyQt4 import QtCore, QtGui\r\n\r\nclass ScribbleArea(QtGui.QWidget):\r\n    def __init__(self, parent=None):\r\n        super(ScribbleArea, self).__init__(parent)\r\n\r\n        self.setAttribute(QtCore.Qt.WA_StaticContents)\r\n        self.modified = False\r\n        self.scribbling = False\r\n        self.myPenWidth = 1\r\n        self.myPenColor = QtCore.Qt.blue\r\n        imageSize = QtCore.QSize(500, 500)\r\n        self.image = QtGui.QImage(imageSize, QtGui.QImage.Format_RGB32)\r\n        self.lastPoint = QtCore.QPoint()\r\n\r\n    def openImage(self, fileName):\r\n        loadedImage = QtGui.QImage()\r\n        if not loadedImage.load(fileName):\r\n            return False\r\n\r\n        w = loadedImage.width()\r\n        h = loadedImage.height()    \r\n        self.mainWindow.resize(w, h)\r\n\r\n        self.image = loadedImage\r\n        self.modified = False\r\n        self.update()\r\n        return True\r\n\r\n    def saveImage(self, fileName, fileFormat):\r\n        visibleImage = self.image\r\n        self.resizeImage(visibleImage, self.size())\r\n\r\n        if visibleImage.save(fileName, fileFormat):\r\n            self.modified = False\r\n            return True\r\n        else:\r\n            return False\r\n\r\n    def setPenColor(self, newColor):\r\n        self.myPenColor = newColor\r\n\r\n    def setPenWidth(self, newWidth):\r\n        self.myPenWidth = newWidth\r\n\r\n    def clearImage(self):\r\n        self.image.fill(QtGui.qRgb(255, 255, 255))\r\n        self.modified = True\r\n        self.update()\r\n\r\n    def mousePressEvent(self, event):\r\n        if event.button() == QtCore.Qt.LeftButton:\r\n            self.lastPoint = event.pos()\r\n            self.scribbling = True\r\n\r\n    def mouseMoveEvent(self, event):\r\n        if (event.buttons() &amp; QtCore.Qt.LeftButton) and self.scribbling:\r\n            self.drawLineTo(event.pos())\r\n\r\n    def mouseReleaseEvent(self, event):\r\n        if event.button() == QtCore.Qt.LeftButton and self.scribbling:\r\n            self.drawLineTo(event.pos())\r\n            self.scribbling = False\r\n\r\n    def paintEvent(self, event):\r\n        painter = QtGui.QPainter(self)\r\n        painter.drawImage(event.rect(), self.image)\r\n\r\n    def resizeEvent(self, event):\r\n\r\n        self.resizeImage(self.image, event.size())\r\n\r\n        super(ScribbleArea, self).resizeEvent(event)\r\n\r\n    def drawLineTo(self, endPoint):\r\n        painter = QtGui.QPainter(self.image)\r\n        painter.setPen(QtGui.QPen(self.myPenColor,\r\n                                  self.myPenWidth,\r\n                                  QtCore.Qt.SolidLine,\r\n                                  QtCore.Qt.RoundCap,\r\n                                  QtCore.Qt.RoundJoin))\r\n        painter.drawLine(self.lastPoint, endPoint)\r\n        self.modified = True\r\n\r\n        self.update()\r\n        self.lastPoint = QtCore.QPoint(endPoint)\r\n\r\n    def resizeImage(self, image, newSize):\r\n        if image.size() == newSize:\r\n            return\r\n        \r\n        newImage = QtGui.QImage(newSize, QtGui.QImage.Format_RGB32)\r\n        newImage.fill(QtGui.qRgb(255, 255, 255))\r\n        painter = QtGui.QPainter(newImage)\r\n        painter.drawImage(QtCore.QPoint(0, 0), image)\r\n\r\n        self.image = newImage\r\n\r\n    def print_(self):\r\n        printer = QtGui.QPrinter(QtGui.QPrinter.HighResolution)\r\n\r\n        printDialog = QtGui.QPrintDialog(printer, self)\r\n        if printDialog.exec_() == QtGui.QDialog.Accepted:\r\n            painter = QtGui.QPainter(printer)\r\n            rect = painter.viewport()\r\n            size = self.image.size()\r\n            size.scale(rect.size(), QtCore.Qt.KeepAspectRatio)\r\n            painter.setViewport(rect.x(), rect.y(), size.width(), size.height())\r\n            painter.setWindow(self.image.rect())\r\n            painter.drawImage(0, 0, self.image)\r\n            painter.end()\r\n\r\n    def isModified(self):\r\n        return self.modified\r\n\r\n    def penColor(self):\r\n        return self.myPenColor\r\n\r\n    def penWidth(self):\r\n        return self.myPenWidth\r\n\r\nclass MainWindow(QtGui.QMainWindow):\r\n    def __init__(self):\r\n        super(MainWindow, self).__init__()\r\n\r\n        self.saveAsActs = []\r\n\r\n        self.scribbleArea = ScribbleArea(self)\r\n        self.scribbleArea.clearImage()\r\n        self.scribbleArea.mainWindow = self  # maybe not using this?\r\n        self.setCentralWidget(self.scribbleArea)\r\n\r\n        self.createActions()\r\n        self.createMenus()\r\n\r\n        self.setWindowTitle(\"Scribble\")\r\n        self.resize(500, 500)\r\n\r\n    def closeEvent(self, event):\r\n        if self.maybeSave():\r\n            event.accept()\r\n        else:\r\n            event.ignore()\r\n\r\n    def open(self):\r\n        if self.maybeSave():\r\n            fileName = QtGui.QFileDialog.getOpenFileName(self, \"Open File\",\r\n                QtCore.QDir.currentPath())\r\n            if fileName:\r\n                self.scribbleArea.openImage(fileName)\r\n\r\n    def save(self):\r\n        action = self.sender()\r\n        fileFormat = action.data()\r\n        self.saveFile(fileFormat)\r\n\r\n    def penColor(self):\r\n        newColor = QtGui.QColorDialog.getColor(self.scribbleArea.penColor())\r\n        if newColor.isValid():\r\n            self.scribbleArea.setPenColor(newColor)\r\n\r\n    def penWidth(self):\r\n        newWidth, ok = QtGui.QInputDialog.getInteger(self, \"Scribble\",\r\n            \"Select pen width:\", self.scribbleArea.penWidth(), 1, 50, 1)\r\n        if ok:\r\n            self.scribbleArea.setPenWidth(newWidth)\r\n\r\n    def about(self):\r\n        QtGui.QMessageBox.about(self, \"About Scribble\",\r\n            \"&lt;p&gt;The &lt;b&gt;Scribble&lt;/b&gt; example shows how to use \"\r\n            \"QMainWindow as the base widget for an application, and how \"\r\n            \"to reimplement some of QWidget\'s event handlers to receive \"\r\n            \"the events generated for the application\'s widgets:&lt;/p&gt;\"\r\n            \"&lt;p&gt; We reimplement the mouse event handlers to facilitate \"\r\n            \"drawing, the paint event handler to update the application \"\r\n            \"and the resize event handler to optimize the application\'s \"\r\n            \"appearance. In addition we reimplement the close event \"\r\n            \"handler to intercept the close events before terminating \"\r\n            \"the application.&lt;/p&gt;\"\r\n            \"&lt;p&gt; The example also demonstrates how to use QPainter to \"\r\n            \"draw an image in real time, as well as to repaint \"\r\n            \"widgets.&lt;/p&gt;\")\r\n\r\n    def createActions(self):\r\n        self.openAct = QtGui.QAction(\"&amp;Open...\", self, shortcut=\"Ctrl+O\",\r\n            triggered=self.open)\r\n\r\n        for format in QtGui.QImageWriter.supportedImageFormats():\r\n            format = str(format)\r\n\r\n            text = format.upper() + \"...\"\r\n\r\n            action = QtGui.QAction(text, self, triggered=self.save)\r\n            action.setData(format)\r\n            self.saveAsActs.append(action)\r\n\r\n        self.printAct = QtGui.QAction(\"&amp;Print...\", self,\r\n            triggered=self.scribbleArea.print_)\r\n\r\n        self.exitAct = QtGui.QAction(\"E&amp;xit\", self, shortcut=\"Ctrl+Q\",\r\n            triggered=self.close)\r\n\r\n        self.penColorAct = QtGui.QAction(\"&amp;Pen Color...\", self,\r\n            triggered=self.penColor)\r\n\r\n        self.penWidthAct = QtGui.QAction(\"Pen &amp;Width...\", self,\r\n            triggered=self.penWidth)\r\n\r\n        self.clearScreenAct = QtGui.QAction(\"&amp;Clear Screen\", self,\r\n            shortcut=\"Ctrl+L\", triggered=self.scribbleArea.clearImage)\r\n\r\n        self.aboutAct = QtGui.QAction(\"&amp;About\", self, triggered=self.about)\r\n\r\n        self.aboutQtAct = QtGui.QAction(\"About &amp;Qt\", self,\r\n            triggered=QtGui.qApp.aboutQt)\r\n\r\n    def createMenus(self):\r\n        self.saveAsMenu = QtGui.QMenu(\"&amp;Save As\", self)\r\n        for action in self.saveAsActs:\r\n            self.saveAsMenu.addAction(action)\r\n\r\n        fileMenu = QtGui.QMenu(\"&amp;File\", self)\r\n        fileMenu.addAction(self.openAct)\r\n        fileMenu.addMenu(self.saveAsMenu)\r\n        fileMenu.addAction(self.printAct)\r\n        fileMenu.addSeparator()\r\n        fileMenu.addAction(self.exitAct)\r\n\r\n        optionMenu = QtGui.QMenu(\"&amp;Options\", self)\r\n        optionMenu.addAction(self.penColorAct)\r\n        optionMenu.addAction(self.penWidthAct)\r\n        optionMenu.addSeparator()\r\n        optionMenu.addAction(self.clearScreenAct)\r\n\r\n        helpMenu = QtGui.QMenu(\"&amp;Help\", self)\r\n        helpMenu.addAction(self.aboutAct)\r\n        helpMenu.addAction(self.aboutQtAct)\r\n\r\n        self.menuBar().addMenu(fileMenu)\r\n        self.menuBar().addMenu(optionMenu)\r\n        self.menuBar().addMenu(helpMenu)\r\n\r\n    def maybeSave(self):\r\n        if self.scribbleArea.isModified():\r\n            ret = QtGui.QMessageBox.warning(self, \"Scribble\",\r\n                \"The image has been modified.\\n\"\r\n                \"Do you want to save your changes?\",\r\n                QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard | \r\n                QtGui.QMessageBox.Cancel)\r\n            if ret == QtGui.QMessageBox.Save:\r\n                return self.saveFile(\'png\')\r\n            elif ret == QtGui.QMessageBox.Cancel:\r\n                return False\r\n\r\n        return True\r\n\r\n    def saveFile(self, fileFormat):\r\n        initialPath = QtCore.QDir.currentPath() + \'/untitled.\' + fileFormat\r\n\r\n        fileName = QtGui.QFileDialog.getSaveFileName(self, \"Save As\",\r\n            initialPath,\r\n            \"%s Files (*.%s);;All Files (*)\" % (fileFormat.upper(), fileFormat))\r\n        if fileName:\r\n            return self.scribbleArea.saveImage(fileName, fileFormat)\r\n\r\n        return False\r\n\r\nif __name__ == \'__main__\':\r\n\r\n    import sys\r\n\r\n    app = QtGui.QApplication(sys.argv)\r\n    window = MainWindow()\r\n    window.show()\r\n    sys.exit(app.exec_())</pre>','',1,1,'2012-05-25 23:09:56','2012-05-25 23:09:56',19,'python, pyqt',NULL),(72,'QToolBox实现QQ抽屉效果','pyqt-qtoolbox-qq',1,'<p>Toolbox 会一层叠着一层地显示出一列的标签页，并且每一个标签页上会显示出一个当前的选中的项。每一个标签页在整列的标签页中会有一个索引位置。标签页中每一都是一个 QWidget</p>\r\n<p>每个项目都有一个 itemText()，一个可选的 itemIcon()，可选的 itemToolTip()，和一个widget()。项目的属性可通过setItemText()，setItemIcon()，and setItemToolTip()函数改变。每个项目可通过各自的 setItemEnabled() 函数启用或禁用。</p>\r\n<p>标签页中的项目可通过 addItem() 函数添加，或者用 insertItem() 函数在特定位置插入。获取所有项目的个数可通过 count() 函数。项目可通过 &nbsp;removeItem() 函数进行移除。组合使用 removeItem() 和 insertItem() 函数可以实现项目在不位置的移动。</p>\r\n<p>当前选中项目的索引位置可通过 currentIndex() 函数获取，也可通过 setCurrentIndex() 函数设置。给定项目的索引位置可通过 indexOf() 方法得到，指定索引的项目可通过 item() 方法得到。</p>\r\n<p>在当前选中项目改变时会发送 currentChanged() 信号。以下是程序代码</p>\r\n<pre class=\"prettyprint\"># -*- coding: utf-8 -*-  \r\nfrom PyQt4 import QtGui, QtCore\r\n  \r\nQtCore.QTextCodec.setCodecForTr(QtCore.QTextCodec.codecForName(\"utf8\"))  \r\n  \r\nclass MyQQ(QtGui.QTabWidget):  \r\n    def __init__(self, parent=None):  \r\n        super(MyQQ, self).__init__(parent)  \r\n          \r\n        toolButton1 = QtGui.QToolButton()  \r\n        toolButton1.setText(self.tr(\"千本桜\"))  \r\n        toolButton1.setIcon(QtGui.QIcon(\"d:/image/1.png\"))\r\n        toolButton1.setIconSize(QtCore.QSize(60, 60))  \r\n        toolButton1.setAutoRaise(True)  \r\n        toolButton1.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)  \r\n  \r\n        toolButton2 = QtGui.QToolButton()  \r\n        toolButton2.setText(self.tr(\"ファイナル　ファンタジー\"))  \r\n        toolButton2.setIcon(QtGui.QIcon(\"d:/image/2.png\")) \r\n        toolButton2.setIconSize(QtCore.QSize(60, 60))  \r\n        toolButton2.setAutoRaise(True)  \r\n        toolButton2.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)         \r\n  \r\n        toolButton3 = QtGui.QToolButton()  \r\n        toolButton3.setText(self.tr(\"朽木\"))  \r\n        toolButton2.setIcon(QtGui.QIcon(\"d:/image/3.png\")) \r\n        toolButton3.setIconSize(QtCore.QSize(60, 60))  \r\n        toolButton3.setAutoRaise(True)  \r\n        toolButton3.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)  \r\n  \r\n        groupbox1 = QtGui.QGroupBox()  \r\n        vlayout1 = QtGui.QVBoxLayout(groupbox1)  \r\n        vlayout1.setMargin(10)  \r\n        vlayout1.setAlignment(QtCore.Qt.AlignCenter)  \r\n        vlayout1.addWidget(toolButton1)  \r\n        vlayout1.addWidget(toolButton2)  \r\n        vlayout1.addStretch()  \r\n  \r\n        groupbox2 = QtGui.QGroupBox()  \r\n        vlayout2 = QtGui.QVBoxLayout(groupbox2)  \r\n        vlayout2.setMargin(10)  \r\n        vlayout2.setAlignment(QtCore.Qt.AlignCenter)  \r\n        vlayout2.addWidget(toolButton3)  \r\n        vlayout2.addStretch()  \r\n  \r\n        groupbox3 = QtGui.QGroupBox()  \r\n  \r\n        toolbox1 = QtGui.QToolBox()  \r\n        toolbox1.addItem(groupbox1, self.tr(\"我的好友\"))  \r\n        toolbox1.addItem(groupbox2, self.tr(\"同事\"))  \r\n        toolbox1.addItem(groupbox3, self.tr(\"黑名单\"))  \r\n                 \r\n        toolbox2 = QtGui.QToolBox()  \r\n          \r\n        self.addTab(toolbox1, self.tr(\"联系人\"))  \r\n        self.addTab(toolbox2, self.tr(\"群/讨论组\"))  \r\n\r\nif __name__ == \'__main__\':\r\n    app = QtGui.QApplication([])\r\n    w = MyQQ()\r\n    w.show()\r\n    app.exec_()</pre>\r\n<p>MyQQ类继承自<a href=\"http://qt-project.org/doc/qt-4.8/qtabwidget.html\">QTabWidget</a>，在QTabWidget的其中一个标签上会放一个QToolBox对象来实现QQ抽屉效果。<a href=\"http://qt-project.org/doc/qt-4.8/qtoolbutton.html\">QToolButton</a>提供了一种快速访问命令或选择项的按钮，通常在工具条中使用。</p>\r\n<p>程序中创建了一个QToolButton类实例，在这里QToolButton分别对应于抽屉中的每一个按钮。按钮创建后便对按钮的文字，图标以及大小等进行设置。当设置按钮的AutoRaise属性为True，即当鼠标离开时，按钮自动恢复成弹起状态。</p>\r\n<p>接下来设置按钮的ToolButtonStyle属性，ToolButtonStyle属性主要用来描述按钮的文字和图标的显示方式。Qt定义了4种QToolButtonStyle类型，分别介绍如下：</p>\r\n<ol>\r\n<li>QtCore.Qt.ToolButtonIconOnly：只显示图标。</li>\r\n<li>QtCore.Qt.ToolButtonTextOnly：只显示文字。</li>\r\n<li>QtCore.Qt.ToolButtonTextBesideIcon：文字显示在图标旁边。</li>\r\n<li>QtCore.Qt.ToolButtonTextUnderIcon：文字显示在图标下面。</li>\r\n</ol>\r\n<p>程序员可以根据显示需要调整显示方式。</p>','',1,1,'2012-06-02 11:44:46','2012-06-02 11:44:46',19,'python, pyqt',NULL),(73,'QTextEdit实例--Calendar','pyqt-qtextedit-calendar',1,'<p>该日历实例展示如何创建富文本及如何展示在富文本编辑器中。</p>\r\n<p style=\"text-align: center;\"><img class=\"aligncenter\" src=\"http://qt-project.org/doc/qt-4.8/images/calendar-example.png\" alt=\"该图片来自Qt官方文档\" width=\"648\" height=\"303\" /></p>\r\n<p>该救命演示如下内容：</p>\r\n<ul>\r\n<li>如何使用 text editor 和 text document</li>\r\n<li>在文档中插入 tables 和 frames</li>\r\n<li>表格的切换</li>\r\n<li>插入不同样式的文本</li>\r\n</ul>\r\n<p>该富文本编辑器会在一个 main window 应用程序中展示文档。</p>\r\n<p>1、定义MainWindow</p>\r\n<p>在MainWindow中提供一个文本编辑器的widget及一些控件让用户来改变年月。在MainWindow构造函数中会设置用户界面及初始化生成Calendar的一些变量。</p>\r\n<pre class=\"prettyprint\">class MyWindow(QtGui.QMainWindow):\r\n    def __init__(self):\r\n        super(MyWindow, self).__init__()\r\n        \r\n        self.selectedDate = QtCore.QDate.currentDate()\r\n        self.fontSize = 10\r\n\r\n        centralWidget = QtGui.QWidget()</pre>\r\n<p>在开始时会默认使用当前日期并高亮显示在Calendar控件中。因为使用了<a href=\"http://qt-project.org/doc/qt-4.8/qmainwindow.html\">QMainWindow</a>来显示用户界面，就要创建一个widget来作为中央显示的widget。</p>\r\n<p>用户界面上会有一行控件显示在日历上方；我们创建一个 label 和 combobox 让用户来选择月份，和一个 spin box 来选择年份。</p>\r\n<pre class=\"prettyprint\">        dateLabel = QtGui.QLabel(self.tr(\"Date:\"))\r\n        monthCombo = QtGui.QComboBox()\r\n        \r\n        for month in range(1, 13):\r\n            # 通过指定的数字获得月份的全称：1对应January， 12对应December\r\n            # 再把全称添加到QComboBox中\r\n            monthCombo.addItem(QtCore.QDate.longMonthName(month))\r\n        \r\n        # QDateTimeEdit是编辑日期和时间的控件\r\n        yearEdit = QtGui.QDateTimeEdit()\r\n        # 只能编辑年份\r\n        yearEdit.setDisplayFormat(\'yyyy\')\r\n        # 显示能够显示的日期范围\r\n        yearEdit.setDateRange(QtCore.QDate(1753, 1, 1), \r\n                              QtCore.QDate(8000, 1, 1))</pre>\r\n<p>我们用 selectedDate 来获取当前年月，然后再把它们设置到 combobox 和 spin box 中。</p>\r\n<p>在 spin box 中显示字体大小，并设置合适的范围以供选择</p>\r\n<pre class=\"prettyprint\">        fontSizeLabel = QtGui.QLabel(\'Font Size:\')\r\n        fontSizeSpinBox = QtGui.QSpinBox()\r\n        fontSizeSpinBox.setRange(1, 64)\r\n        \r\n        self.editor = QtGui.QTextBrowser()\r\n        self.insertCalendar()</pre>\r\n<p>我们使用 insertCalendar 创建日历。每张日历都会显示在同一个 Calendar 中；在该示例中我们使用了 <a href=\"http://qt-project.org/doc/qt-4.8/qtextbrowser.html\">QTextBrowser</a>，因为我们不允许控件被编辑。</p>\r\n<p>要使这些控件能够设置月份，年份和字体大小我们还需要连接到相应的信号及对应的处理函数</p>\r\n<pre class=\"prettyprint\">        self.connect(monthCombo, \r\n                     QtCore.SIGNAL(\'activated(int)\'), \r\n                     self.setMonth)\r\n        self.connect(yearEdit, \r\n                     QtCore.SIGNAL(\'dateChanged(QDate)\'), \r\n                     self.setYear)\r\n        self.connect(fontSizeSpinBox, \r\n                     QtCore.SIGNAL(\'valueChanged(int)\'), \r\n                     self.setFontSize)</pre>\r\n<p>我们创建 layouts 去管理这些已经创建好的 widgets。</p>\r\n<pre class=\"prettyprint\">        controlsLayout = QtGui.QHBoxLayout()\r\n        controlsLayout.addWidget(dateLabel)\r\n        controlsLayout.addWidget(monthCombo)\r\n        controlsLayout.addWidget(yearEdit)\r\n        # 在设置日期与设置字体大小之间添加空白\r\n        controlsLayout.addSpacing(24)\r\n        controlsLayout.addWidget(fontSizeLabel)\r\n        controlsLayout.addWidget(fontSizeSpinBox)\r\n        controlsLayout.addStretch(1)\r\n        \r\n        centralLayout = QtGui.QVBoxLayout()\r\n        centralLayout.addLayout(controlsLayout)\r\n        centralLayout.addWidget(self.editor, 1)\r\n        centralWidget.setLayout(centralLayout)\r\n        \r\n        self.resize(450, 250)\r\n        self.setCentralWidget(centralWidget)</pre>\r\n<p>每张日历都是通过 insertCalendar() 函数创建并使用设置好的日期和字体大小。</p>\r\n<pre class=\"prettyprint\">    def insertCalendar(self):\r\n        self.editor.clear()\r\n        cursor = self.editor.textCursor()\r\n        # 开始一个 block 的编辑，这会些在 撤销/重复 操作中作为单个的动作\r\n        cursor.beginEditBlock()\r\n        \r\n        date = QtCore.QDate(self.selectedDate.year(), \r\n                            self.selectedDate.month(), \r\n                            1)</pre>\r\n<p>在开始时会清除编辑器中的所有文档，然后从编辑器中获取 text cursor 用来添加内容。然后创建基于 self.selectedDate 的 <a href=\"http://qt-project.org/doc/qt-4.8/qdate.html\">QDate</a> 对象</p>\r\n<p>该日历是通过 table 构建的，并带有灰色背景和一共7列： 分别为一个星期的7天。它会被设置在页面的中间。所有的这些属性都在 <a href=\"http://qt-project.org/doc/qt-4.8/qtexttableformat.html\">QTextTableFormat</a> 对象中进行设置。</p>\r\n<pre class=\"prettyprint\">        tableFormat = QtGui.QTextTableFormat()\r\n        tableFormat.setAlignment(QtCore.Qt.AlignCenter)\r\n        tableFormat.setBackground(QtGui.QColor(\"#e0e0e0\"))\r\n        tableFormat.setCellPadding(2)\r\n        tableFormat.setCellSpacing(4)</pre>\r\n<p>表格中的每个单元格都会设置 padding 和 spacing 以方便阅读。</p>\r\n<p>我们想要为每列设置相同的宽度，所以我们提供了一个设置了每一列宽度百分比的列表并设置到<a href=\"http://qt-project.org/doc/qt-4.8/qtexttableformat.html\">QTextTableFormat</a>中：</p>\r\n<pre class=\"prettyprint\">        lstTextLength = [QtGui.QTextLength(QtGui.QTextLength.PercentageLength, 14),\r\n                         QtGui.QTextLength(QtGui.QTextLength.PercentageLength, 14),\r\n                         QtGui.QTextLength(QtGui.QTextLength.PercentageLength, 14),\r\n                         QtGui.QTextLength(QtGui.QTextLength.PercentageLength, 14),\r\n                         QtGui.QTextLength(QtGui.QTextLength.PercentageLength, 14),\r\n                         QtGui.QTextLength(QtGui.QTextLength.PercentageLength, 14),\r\n                         QtGui.QTextLength(QtGui.QTextLength.PercentageLength, 14),\r\n                         ]\r\n        tableFormat.setColumnWidthConstraints(lstTextLength)</pre>\r\n<p>设置列宽的约束只有在表格的列数跟 format 中定义的列的数量相同时才有效。 我们在当前光标位置创建了一个有1行和7列的新表格。</p>\r\n<pre class=\"prettyprint\">        table = cursor.insertTable(1, 7, tableFormat)</pre>\r\n<p>我们只需要在一开始时添加一行，其余的在需要时再添加。这种方法意味着我们不需要计算每个日期直到在添加单元格。</p>\r\n<p>当使用 cursor 的插入函数把内容插入到文档时，光标会自动移动到新插入的对象上。这意味着我们可以立即修改表格中的内容。</p>\r\n<pre class=\"prettyprint\">        frame = cursor.currentFrame()\r\n        frameFormat = frame.frameFormat()\r\n        frameFormat.setBorder(1)\r\n        frame.setFrameFormat(frameFormat)</pre>\r\n<p>因为表格都有一个外边框，所以我们可以获取其边框和边框格式并进行自定义。在修改成我们想要的格式后，就可以用我们修改过的格式来设置表格的边框格式。我们为外边框设置了一个像素的宽度。</p>\r\n<pre class=\"prettyprint\">        format = cursor.charFormat()\r\n        format.setFontPointSize(self.fontSize)\r\n        \r\n        boldFormat = cursor.charFormat()\r\n        boldFormat.setFontPointSize(self.fontSize)\r\n        boldFormat.setFontWeight(QtGui.QFont.Bold)\r\n        \r\n        highlightedFormat = cursor.charFormat()\r\n        highlightedFormat.setFontPointSize(self.fontSize)\r\n        highlightedFormat.setFontWeight(QtGui.QFont.Bold)\r\n        highlightedFormat.setBackground(QtCore.Qt.yellow)</pre>\r\n<p>通过同样的方法，我们获取当前光标位置的字符格式，创建自定义格式。</p>\r\n<p>我们没有直接把格式设置在光标上，因为这样会改变默认的格式。而是在插入文本时明确的指定为自定义的格式。以下的循环用粗体格式把一个星期中的每一天插入到表格中。</p>\r\n<pre class=\"prettyprint\">        for weekDay in range(1, 8):\r\n            cell = table.cellAt(0, weekDay - 1)</pre>\r\n<p>我们使用 cellAt() 函数获取表格中第一行(row 0)中的单元格。</p>\r\n<p>在插入文本到单元格之前，我们必须获取文档中正确位置的光标。单元格中正好提供了这样的函数，我们用从单元格中获取的光标并用之前已经定义好的 boldFormat 字符格式来插入文本。</p>\r\n<pre class=\"prettyprint\">            cellCursor = cell.firstCursorPosition()\r\n            # 插入固定的星期列，分别为星期一到星期日\r\n            cellCursor.insertText(\'%s\' % (QtCore.QDate.longDayName(weekDay),),\r\n                                  boldFormat)</pre>\r\n<p>插入文本到文档对象中通常会遵循相同的模式。每个对象都会提供一个对象内初始化位置的光标，并被用来插入新的内容。我们还会继续使用该模式来插入月份中的每一天。</p>\r\n<p>因为每个月都会大于7天，我们会先插入一行然后一直添加天数直到一个月的最后一天。如果到了当前天，就会插入指定的格式（highlightedFormat）的文本来突出显示。</p>\r\n<pre class=\"prettyprint\">        table.insertRows(table.rows(), 1)</pre>\r\n<p>在第一行（星期行）的后面我们会插入一条新行到表格中。</p>\r\n<p>以下是完整代码：</p>\r\n<pre class=\"prettyprint\"># -*- coding: UTF-8 -*-\r\nfrom PyQt4 import QtGui, QtCore\r\n\r\nclass MyWindow(QtGui.QMainWindow):\r\n    def __init__(self):\r\n        super(MyWindow, self).__init__()\r\n        \r\n        self.selectedDate = QtCore.QDate.currentDate()\r\n        self.fontSize = 10\r\n        \r\n        centralWidget = QtGui.QWidget()\r\n        \r\n        dateLabel = QtGui.QLabel(self.tr(\"Date:\"))\r\n        monthCombo = QtGui.QComboBox()\r\n        \r\n        for month in range(1, 13):\r\n            # 通过指定的数字获得月份的全称：1对应January， 12对应December\r\n            # 再把全称添加到QComboBox中\r\n            monthCombo.addItem(QtCore.QDate.longMonthName(month))\r\n        \r\n        # QDateTimeEdit是编辑日期和时间的控件\r\n        yearEdit = QtGui.QDateTimeEdit()\r\n        # 只能编辑年份\r\n        yearEdit.setDisplayFormat(\'yyyy\')\r\n        # 显示能够显示的日期范围\r\n        yearEdit.setDateRange(QtCore.QDate(1753, 1, 1), \r\n                              QtCore.QDate(8000, 1, 1))\r\n        \r\n        monthCombo.setCurrentIndex(self.selectedDate.month() - 1)\r\n        yearEdit.setDate(self.selectedDate)\r\n        \r\n        fontSizeLabel = QtGui.QLabel(\'Font Size:\')\r\n        fontSizeSpinBox = QtGui.QSpinBox()\r\n        fontSizeSpinBox.setRange(1, 64)\r\n        \r\n        self.editor = QtGui.QTextBrowser()\r\n        self.insertCalendar()\r\n        \r\n        self.connect(monthCombo, \r\n                     QtCore.SIGNAL(\'activated(int)\'), \r\n                     self.setMonth)\r\n        self.connect(yearEdit, \r\n                     QtCore.SIGNAL(\'dateChanged(QDate)\'), \r\n                     self.setYear)\r\n        self.connect(fontSizeSpinBox, \r\n                     QtCore.SIGNAL(\'valueChanged(int)\'), \r\n                     self.setFontSize)\r\n\r\n        controlsLayout = QtGui.QHBoxLayout()\r\n        controlsLayout.addWidget(dateLabel)\r\n        controlsLayout.addWidget(monthCombo)\r\n        controlsLayout.addWidget(yearEdit)\r\n        # 在设置日期与设置字体大小之间添加空白\r\n        controlsLayout.addSpacing(24)\r\n        controlsLayout.addWidget(fontSizeLabel)\r\n        controlsLayout.addWidget(fontSizeSpinBox)\r\n        controlsLayout.addStretch(1)\r\n        \r\n        centralLayout = QtGui.QVBoxLayout()\r\n        centralLayout.addLayout(controlsLayout)\r\n        centralLayout.addWidget(self.editor, 1)\r\n        centralWidget.setLayout(centralLayout)\r\n        \r\n        self.resize(450, 250)\r\n        self.setCentralWidget(centralWidget)\r\n\r\n    def insertCalendar(self):\r\n        self.editor.clear()\r\n        cursor = self.editor.textCursor()\r\n        # 开始一个 block 的编辑，这会些在 撤销/重复 操作中作为单个的动作\r\n        cursor.beginEditBlock()\r\n        \r\n        date = QtCore.QDate(self.selectedDate.year(), \r\n                            self.selectedDate.month(), \r\n                            1)\r\n        \r\n        tableFormat = QtGui.QTextTableFormat()\r\n        tableFormat.setAlignment(QtCore.Qt.AlignCenter)\r\n        tableFormat.setBackground(QtGui.QColor(\"#e0e0e0\"))\r\n        tableFormat.setCellPadding(2)\r\n        tableFormat.setCellSpacing(4)\r\n        \r\n        lstTextLength = [QtGui.QTextLength(QtGui.QTextLength.PercentageLength, 14),\r\n                         QtGui.QTextLength(QtGui.QTextLength.PercentageLength, 14),\r\n                         QtGui.QTextLength(QtGui.QTextLength.PercentageLength, 14),\r\n                         QtGui.QTextLength(QtGui.QTextLength.PercentageLength, 14),\r\n                         QtGui.QTextLength(QtGui.QTextLength.PercentageLength, 14),\r\n                         QtGui.QTextLength(QtGui.QTextLength.PercentageLength, 14),\r\n                         QtGui.QTextLength(QtGui.QTextLength.PercentageLength, 14),\r\n                         ]\r\n        tableFormat.setColumnWidthConstraints(lstTextLength)\r\n        \r\n        table = cursor.insertTable(1, 7, tableFormat)\r\n        \r\n        frame = cursor.currentFrame()\r\n        frameFormat = frame.frameFormat()\r\n        frameFormat.setBorder(1)\r\n        frame.setFrameFormat(frameFormat)\r\n        \r\n        format = cursor.charFormat()\r\n        format.setFontPointSize(self.fontSize)\r\n        \r\n        boldFormat = cursor.charFormat()\r\n        boldFormat.setFontPointSize(self.fontSize)\r\n        boldFormat.setFontWeight(QtGui.QFont.Bold)\r\n        \r\n        highlightedFormat = cursor.charFormat()\r\n        highlightedFormat.setFontPointSize(self.fontSize)\r\n        highlightedFormat.setFontWeight(QtGui.QFont.Bold)\r\n        highlightedFormat.setBackground(QtCore.Qt.yellow)\r\n        \r\n        for weekDay in range(1, 8):\r\n            cell = table.cellAt(0, weekDay - 1)\r\n            cellCursor = cell.firstCursorPosition()\r\n            # 插入固定的星期列，分别为星期一到星期日\r\n            cellCursor.insertText(\'%s\' % (QtCore.QDate.longDayName(weekDay),),\r\n                                  boldFormat)\r\n\r\n        table.insertRows(table.rows(), 1)\r\n        \r\n        while date.month() == self.selectedDate.month():\r\n            weekDay = date.dayOfWeek()\r\n            cell = table.cellAt(table.rows() - 1, weekDay - 1)\r\n            cellCursor = cell.firstCursorPosition()\r\n            \r\n            if date == QtCore.QDate.currentDate():\r\n                cellCursor.insertText(\'%s\' % (date.day(),), highlightedFormat)\r\n            else:\r\n                cellCursor.insertText(\'%s\' % (date.day(),), format)\r\n            \r\n            date = date.addDays(1)\r\n            if weekDay == 7 and date.month() == self.selectedDate.month():\r\n                table.insertRows(table.rows(), 1)\r\n        \r\n        cursor.endEditBlock()\r\n        self.setWindowTitle(\'Calendar for %s %s\' % \r\n                            (QtCore.QDate.longMonthName(self.selectedDate.month()),\r\n                             self.selectedDate.year()))\r\n    \r\n    def setFontSize(self, size):\r\n        self.fontSize = size\r\n        self.insertCalendar()\r\n        \r\n    def setMonth(self, month):\r\n        self.selectedDate = QtCore.QDate(self.selectedDate.year(),\r\n                                         month + 1,\r\n                                         self.selectedDate.day())\r\n        self.insertCalendar()\r\n    \r\n    def setYear(self, date):\r\n        self.selectedDate = QtCore.QDate(date.year(),\r\n                                         self.selectedDate.month(),\r\n                                         self.selectedDate.day())\r\n        self.insertCalendar()\r\n\r\nif __name__ == \'__main__\':\r\n    app = QtGui.QApplication([])\r\n    w = MyWindow()\r\n    w.show()\r\n    app.exec_()</pre>','',1,1,'2012-06-10 12:48:55','2012-06-10 12:48:55',19,'python, pyqt',NULL),(74,'QTextEdit实例--订货单程序','pyqt-qtextedit-orderform',1,'<p>该订单实例演示如何通过组合简单模板和对话框中用户输入的数据来生成富文本文档。</p>\r\n<p>数据会从对话框对象抽取并通过QTextCursor显示到QTextEdit中，并使用了许多 format。每张订单会添加到 <a href=\"http://qt-project.org/doc/qt-4.8/qtabwidget.html\">QTabWidget</a> 的一个标签上。</p>\r\n<p style=\"text-align: center;\"><img class=\"aligncenter\" src=\"http://qt-project.org/doc/qt-4.8/images/orderform-example.png\" alt=\"订单程序画面截图\" width=\"648\" height=\"514\" /></p>\r\n<p>定义对话框：</p>\r\n<p>DetailsDialog类是 <a href=\"http://qt-project.org/doc/qt-4.8/qdialog.html\">QDialog</a> 类的子类，其构造函数接收两个参数，分别是 title 和 parent。该类还定义了4个获取数据的函数： orderItmes()、senderName()、senderAddress()和sendOffers() 。</p>\r\n<p style=\"text-align: center;\"><img class=\"aligncenter\" src=\"http://qt-project.org/doc/qt-4.8/images/orderform-example-detailsdialog.png\" alt=\"添加表彰的对话框\" width=\"597\" height=\"329\" /></p>\r\n<p>DetailsDialog类的实现</p>\r\n<p>在构造函数中定义了两个输入字段和它们各自的Label。setupItemsTable()函数被用来生成表格时所需要的数据。通过 OK 和 Cancel 两个按钮来说明如何使用 QDialogButtonBox 对象。</p>\r\n<pre class=\"prettyprint\">class DetailDialog(QtGui.QDialog):\r\n    def __init__(self, title, parent):\r\n        super(DetailDialog, self).__init__(parent)\r\n        \r\n        self.nameLabel = QtGui.QLabel(self.tr(\"Name:\"))\r\n        self.addressLabel = QtGui.QLabel(self.tr(\"Address:\"))\r\n        self.addressLabel.setAlignment(QtCore.Qt.AlignLeft \r\n                                     | QtCore.Qt.AlignTop)\r\n        \r\n        self.nameEdit = QtGui.QLineEdit()\r\n        self.addressEdit = QtGui.QTextEdit()\r\n        self.offersCheckBox = \\\r\n            QtGui.QCheckBox(self.tr(\'Send information about\'\r\n                                    \'products and special offers\'))\r\n        self.setupItemsTable()\r\n        \r\n        self.buttonBox = \\\r\n            QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok\r\n                                 | QtGui.QDialogButtonBox.Cancel)\r\n        \r\n        self.connect(self.buttonBox, \r\n                     QtCore.SIGNAL(\'accepted()\'), \r\n                     self.verify)\r\n        self.connect(self.buttonBox, \r\n                     QtCore.SIGNAL(\'rejected()\'), \r\n                     QtCore.SLOT(\'reject()\'))</pre>\r\n<p>所有的对象都放在了<a href=\"http://qt-project.org/doc/qt-4.8/qgridlayout.html\">QGridLayout</a> 中</p>\r\n<pre class=\"prettyprint\">        widgetSubscriber = QtGui.QWidget()\r\n        subscriberLayout = QtGui.QGridLayout()\r\n        subscriberLayout.addWidget(self.nameLabel, 0, 0)\r\n        subscriberLayout.addWidget(self.nameEdit, 0, 1)\r\n        subscriberLayout.addWidget(self.addressLabel, 1, 0)\r\n        subscriberLayout.addWidget(self.addressEdit, 1, 1)\r\n        widgetSubscriber.setLayout(subscriberLayout)\r\n        \r\n        widgetOrder = QtGui.QWidget()\r\n        orderLayout = QtGui.QVBoxLayout()\r\n        orderLayout.addWidget(self.itemsTable)\r\n        widgetOrder.setLayout(orderLayout)\r\n        \r\n        tabWidget = QtGui.QTabWidget()\r\n        tabWidget.addTab(widgetSubscriber, \"Subscriber\")\r\n        tabWidget.addTab(widgetOrder, \"Order\")\r\n        \r\n        mainLayout = QtGui.QGridLayout()\r\n        mainLayout.addWidget(tabWidget)\r\n        mainLayout.addWidget(self.buttonBox)\r\n        self.setLayout(mainLayout)\r\n        \r\n        self.setWindowTitle(title)</pre>\r\n<p>setupItemsTable()函数演示了 <a href=\"http://qt-project.org/doc/qt-4.8/qtablewidget.html\">QTableWidget</a> 对象，设置了行数以及订单的数据。共设置了2列，分别是\"name\" 和 \"quantity\"。在 for 循环中填充 itemsTable，name 项的 flag 被设置为 Qt.ItemIsEnabled 和 Qt.ItemIsSelectable，表示该项只能被选择，而不能被编辑。而 quantity 项被初始设置为1，它可以在运行时修改其内容。</p>\r\n<pre class=\"prettyprint\">    def setupItemsTable(self):\r\n        self.items = [\'T-shirt\', \'Badge\', \'Reference book\']\r\n        self.itemsTable = QtGui.QTableWidget(len(self.items), 2)\r\n        row = 0\r\n        for row in range(len(self.items)):\r\n            name = QtGui.QTableWidgetItem(self.items[row])\r\n            name.setFlags(QtCore.Qt.ItemIsEnabled \r\n                        | QtCore.Qt.ItemIsSelectable)\r\n            self.itemsTable.setItem(row, 0, name)\r\n            quantity = QtGui.QTableWidgetItem(\'1\')\r\n            self.itemsTable.setItem(row, 1, quantity)</pre>\r\n<p>orderItems()函数从 itemsTable 中抽取数据并封闭到list中返回</p>\r\n<pre class=\"prettyprint\">    def orderItems(self):\r\n        orderList = []\r\n        for row in range(len(self.items)):\r\n            lst = []\r\n            lst.append(self.itemsTable.item(row, 0).text())\r\n            quantity = self.itemsTable.item(row, 1).text()\r\n            lst.append(max(0, quantity))\r\n            orderList.append(lst)\r\n        return orderList</pre>\r\n<p>senderName()函数用来获取 QLineEdit 中保存的发出订单的人名：</p>\r\n<pre class=\"prettyprint\">    def senderName(self):\r\n        return self.nameEdit.text()</pre>\r\n<p>senderAddress()函数用来返回 QTextEdit 中地址</p>\r\n<pre class=\"prettyprint\">    def senderAddress(self):\r\n        return self.addressEdit.toPlainText()</pre>\r\n<p>verify()函数用来验证用户在 DetailDialog 对话框中输入的详细信息。如果输入的信息不完整的话，会显示一个 QMessageBox 来提示用户是否在放弃。否则就会调用 accept() 函数来接收当前的输入。</p>\r\n<pre class=\"prettyprint\">    def verify(self):\r\n        if not self.nameEdit.text().isEmpty() and \\\r\n            not self.addressEdit.toPlainText().isEmpty():\r\n            self.accept()\r\n            return\r\n        \r\n        answer = QtGui.QMessageBox.warning(self,\r\n           self.tr(\'Incomplete Form\'),\r\n           self.tr(\'The form does not contain all the necessary information.\\n\'\r\n                   \'Do you want to discard it?\'),\r\n           QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)\r\n        if answer == QtGui.QMessageBox.Yes:\r\n            self.reject()</pre>\r\n<p>MainWindow 的实现</p>\r\n<pre class=\"prettyprint\"># -*- coding: UTF-8 -*-\r\nfrom PyQt4 import QtGui, QtCore\r\n\r\nclass DetailDialog(QtGui.QDialog):\r\n    def __init__(self, title, parent):\r\n        super(DetailDialog, self).__init__(parent)\r\n        \r\n        self.nameLabel = QtGui.QLabel(self.tr(\"Name:\"))\r\n        self.addressLabel = QtGui.QLabel(self.tr(\"Address:\"))\r\n        self.addressLabel.setAlignment(QtCore.Qt.AlignLeft \r\n                                     | QtCore.Qt.AlignTop)\r\n        \r\n        self.nameEdit = QtGui.QLineEdit()\r\n        self.addressEdit = QtGui.QTextEdit()\r\n        self.offersCheckBox = \\\r\n            QtGui.QCheckBox(self.tr(\'Send information about\'\r\n                                    \'products and special offers\'))\r\n        self.setupItemsTable()\r\n        \r\n        self.buttonBox = \\\r\n            QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok\r\n                                 | QtGui.QDialogButtonBox.Cancel)\r\n        \r\n        self.connect(self.buttonBox,\r\n                     QtCore.SIGNAL(\'accepted()\'),\r\n                     self.verify)\r\n        self.connect(self.buttonBox,\r\n                     QtCore.SIGNAL(\'rejected()\'),\r\n                     QtCore.SLOT(\'reject()\'))\r\n        \r\n        widgetSubscriber = QtGui.QWidget()\r\n        subscriberLayout = QtGui.QGridLayout()\r\n        subscriberLayout.addWidget(self.nameLabel, 0, 0)\r\n        subscriberLayout.addWidget(self.nameEdit, 0, 1)\r\n        subscriberLayout.addWidget(self.addressLabel, 1, 0)\r\n        subscriberLayout.addWidget(self.addressEdit, 1, 1)\r\n        widgetSubscriber.setLayout(subscriberLayout)\r\n        \r\n        widgetOrder = QtGui.QWidget()\r\n        orderLayout = QtGui.QVBoxLayout()\r\n        orderLayout.addWidget(self.itemsTable)\r\n        widgetOrder.setLayout(orderLayout)\r\n        \r\n        tabWidget = QtGui.QTabWidget()\r\n        tabWidget.addTab(widgetSubscriber, \"Subscriber\")\r\n        tabWidget.addTab(widgetOrder, \"Order\")\r\n        \r\n        mainLayout = QtGui.QGridLayout()\r\n        mainLayout.addWidget(tabWidget)\r\n        mainLayout.addWidget(self.buttonBox)\r\n        self.setLayout(mainLayout)\r\n        \r\n        self.setWindowTitle(title)\r\n    \r\n    def setupItemsTable(self):\r\n        self.items = [\'T-shirt\', \'Badge\', \'Reference book\']\r\n        self.itemsTable = QtGui.QTableWidget(len(self.items), 2)\r\n        row = 0\r\n        for row in range(len(self.items)):\r\n            name = QtGui.QTableWidgetItem(self.items[row])\r\n            name.setFlags(QtCore.Qt.ItemIsEnabled \r\n                        | QtCore.Qt.ItemIsSelectable)\r\n            self.itemsTable.setItem(row, 0, name)\r\n            quantity = QtGui.QTableWidgetItem(\'1\')\r\n            self.itemsTable.setItem(row, 1, quantity)\r\n    \r\n    def orderItems(self):\r\n        orderList = []\r\n        for row in range(len(self.items)):\r\n            lst = []\r\n            lst.append(self.itemsTable.item(row, 0).text())\r\n            quantity = self.itemsTable.item(row, 1).text()\r\n            lst.append(max(0, quantity))\r\n            orderList.append(lst)\r\n        return orderList\r\n    \r\n    def senderName(self):\r\n        return self.nameEdit.text()\r\n    \r\n    def senderAddress(self):\r\n        return self.addressEdit.toPlainText()\r\n    \r\n    def sendOffers(self):\r\n        return self.offersCheckBox.isChecked()\r\n    \r\n    def verify(self):\r\n        if not self.nameEdit.text().isEmpty() and \\\r\n            not self.addressEdit.toPlainText().isEmpty():\r\n            self.accept()\r\n            return\r\n        \r\n        answer = QtGui.QMessageBox.warning(self,\r\n           self.tr(\'Incomplete Form\'),\r\n           self.tr(\'The form does not contain all the necessary information.\\n\'\r\n                   \'Do you want to discard it?\'),\r\n           QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)\r\n        if answer == QtGui.QMessageBox.Yes:\r\n            self.reject()\r\n\r\nclass MainWindow(QtGui.QMainWindow):\r\n    def __init__(self):\r\n        super(MainWindow, self).__init__()\r\n        self.setMinimumSize(700, 500)\r\n        \r\n        fileMenu = QtGui.QMenu(self.tr(\"&amp;File\"), self)\r\n        newAction = fileMenu.addAction(self.tr(\"&amp;New...\"))\r\n        newAction.setShortcuts(QtGui.QKeySequence.New)\r\n        \r\n        self.printAction = fileMenu.addAction(self.tr(\"&amp;Print...\"))\r\n        self.printAction.setEnabled(False)\r\n        \r\n        quitAction = fileMenu.addAction(self.tr(\"E&amp;xit\"))\r\n        quitAction.setShortcuts(QtGui.QKeySequence.Quit)\r\n        self.menuBar().addMenu(fileMenu)\r\n        \r\n        self.letters = QtGui.QTabWidget()\r\n        \r\n        self.connect(newAction,\r\n                     QtCore.SIGNAL(\'triggered()\'),\r\n                     self.openDialog)\r\n        self.connect(self.printAction,\r\n                     QtCore.SIGNAL(\'triggered()\'),\r\n                     self.printFile)\r\n        self.connect(quitAction,\r\n                     QtCore.SIGNAL(\'triggered()\'),\r\n                     QtCore.SLOT(\'close()\'))\r\n        \r\n        self.setCentralWidget(self.letters)\r\n        self.setWindowTitle(self.tr(\"Order Form\"))\r\n    \r\n    def createLetter(self, name, address, orderItems, sendOffers):\r\n        editor = QtGui.QTextEdit()\r\n        tabIndex = self.letters.addTab(editor, name)\r\n        self.letters.setCurrentIndex(tabIndex)\r\n        \r\n        cursor = editor.textCursor()\r\n        cursor.movePosition(QtGui.QTextCursor.Start)\r\n        \r\n        topFrame = cursor.currentFrame()\r\n        topFrameFormat = topFrame.frameFormat()\r\n        topFrameFormat.setPadding(16)\r\n        topFrame.setFrameFormat(topFrameFormat)\r\n        \r\n        textFormat = QtGui.QTextCharFormat()\r\n        boldFormat = QtGui.QTextCharFormat()\r\n        boldFormat.setFontWeight(QtGui.QFont.Bold)\r\n        \r\n        referenceFrameFormat = QtGui.QTextFrameFormat()\r\n        referenceFrameFormat.setBorder(1)\r\n        referenceFrameFormat.setPadding(8)\r\n        referenceFrameFormat.setPosition(QtGui.QTextFrameFormat.FloatRight)\r\n        referenceFrameFormat.setWidth(\r\n            QtGui.QTextLength(QtGui.QTextLength.PercentageLength, 40))\r\n        cursor.insertFrame(referenceFrameFormat)\r\n        \r\n        cursor.insertText(\'A company\', boldFormat)\r\n        cursor.insertBlock()\r\n        cursor.insertText(\'321 City Street\')\r\n        cursor.insertBlock()\r\n        cursor.insertText(\'Industry Park\')\r\n        cursor.insertBlock()\r\n        cursor.insertText(\'Another country\')\r\n        \r\n        cursor.setPosition(topFrame.lastPosition())\r\n        \r\n        cursor.insertText(name, textFormat)\r\n        for line in address.split(\'\\n\'):\r\n            cursor.insertBlock()\r\n            cursor.insertText(line)\r\n        \r\n        cursor.insertBlock()\r\n        cursor.insertBlock()\r\n        \r\n        date = QtCore.QDate.currentDate()\r\n        cursor.insertText(self.tr(\'Date: %s\' % (str(date),)), textFormat)\r\n        cursor.insertBlock()\r\n        \r\n        bodyFrameFormat = QtGui.QTextFrameFormat()\r\n        bodyFrameFormat.setWidth(\r\n            QtGui.QTextLength(QtGui.QTextLength.PercentageLength, 100))\r\n        cursor.insertFrame(bodyFrameFormat)\r\n        \r\n        cursor.insertText(\"I would like to place an order \"\r\n                          \"for the following items:\", textFormat)\r\n        cursor.insertBlock()\r\n        cursor.insertBlock()\r\n\r\n        orderTableFormat = QtGui.QTextTableFormat()\r\n        orderTableFormat.setAlignment(QtCore.Qt.AlignCenter)\r\n        orderTable = cursor.insertTable(1, 2, orderTableFormat)\r\n        \r\n        orderTableFormat = cursor.currentFrame().frameFormat()\r\n        orderTableFormat.setBorder(1)\r\n        cursor.currentFrame().setFrameFormat(orderTableFormat)\r\n        \r\n        cursor = orderTable.cellAt(0, 0).firstCursorPosition()\r\n        cursor.insertText(self.tr(\'Product\'), boldFormat)\r\n        cursor = orderTable.cellAt(0, 1).firstCursorPosition()\r\n        cursor.insertText(self.tr(\'Quantity\'), boldFormat)\r\n        \r\n        for i in range(len(orderItems)):\r\n            item = orderItems[i]\r\n            row = orderTable.rows()\r\n            orderTable.insertRows(row, 1)\r\n            cursor = orderTable.cellAt(row, 0).firstCursorPosition()\r\n            cursor.insertText(item[0], textFormat)\r\n            cursor = orderTable.cellAt(row, 1).firstCursorPosition()\r\n            cursor.insertText(\'%s\' % (item[1],) , textFormat)\r\n        \r\n        cursor.setPosition(topFrame.lastPosition())\r\n        cursor.insertBlock()\r\n        cursor.insertText(self.tr(\'Please update my records to take account of the\'\r\n                                  \'following privacy information:\'))\r\n        cursor.insertBlock()\r\n        \r\n        offersTable = cursor.insertTable(2, 2)\r\n        cursor = offersTable.cellAt(0, 1).firstCursorPosition()\r\n        cursor.insertText(self.tr(\"I want to receive more information \"\r\n                                  \"about your company\'s products \"\r\n                                  \"and special offers.\"), textFormat)\r\n        cursor = offersTable.cellAt(1, 1).firstCursorPosition()\r\n        cursor.insertText(self.tr(\"I do not want to receive \"\r\n                                  \"any promotional information \"\r\n                                  \"from your company.\"), textFormat)\r\n        \r\n        if (sendOffers):\r\n            cursor = offersTable.cellAt(0, 0).firstCursorPosition()\r\n        else:\r\n            cursor = offersTable.cellAt(1, 0).firstCursorPosition()\r\n\r\n        cursor.insertText(\"X\", boldFormat)\r\n        \r\n        cursor.setPosition(topFrame.lastPosition())\r\n        cursor.insertBlock()\r\n        cursor.insertText(self.tr(\"Sincerely,\"), textFormat)\r\n        cursor.insertBlock()\r\n        cursor.insertBlock()\r\n        cursor.insertBlock()\r\n        cursor.insertText(name)\r\n\r\n        self.printAction.setEnabled(True)\r\n    \r\n    def createSample(self):\r\n        self.dialog = DetailDialog(\'Dialog with default values\', self)\r\n        self.createLetter(\'Mr. Smith\',\r\n                          \'12 High Street\\nSmall Town\\nThis country\',\r\n                          self.dialog.orderItems(), True)\r\n    \r\n    def openDialog(self):\r\n        dialog = DetailDialog(\"Enter Customer Details\", self)\r\n        if  dialog.exec_() == QtGui.QDialog.Accepted:\r\n            self.createLetter(dialog.senderName(),\r\n                              dialog.senderAddress(),\r\n                              dialog.orderItems(),\r\n                              dialog.sendOffers())\r\n    \r\n    def printFile(self):\r\n        editor = self.letters.currentWidget()\r\n        printer = QtGui.QPrinter()\r\n        \r\n        dialog = QtGui.QPrintDialog(printer, self)\r\n        dialog.setWindowTitle(\'Print Document\')\r\n        if editor.textCursor().hasSelection():\r\n            dialog.addEnabledOption(QtGui.QAbstractPrintDialog.PrintSelection)\r\n        if dialog.exec_() != QtGui.QDialog.Accepted:\r\n            return\r\n        \r\n        editor.print_(printer)\r\n\r\nif __name__ == \'__main__\':\r\n    app = QtGui.QApplication([])\r\n    w = MainWindow()\r\n    w.show()\r\n    w.createSample()\r\n    app.exec_()</pre>\r\n<p>&nbsp;</p>','',1,1,'2012-06-23 18:27:09','2012-06-23 18:27:09',19,'pyqt, python',NULL),(75,'QTextEdit实例--语法高亮','pyqt-qtextedit-syntaxhighlighter',1,'<p>该语法高亮实例演示如何通过 <a href=\"http://qt-project.org/doc/qt-4.8/qsyntaxhighlighter.html\">QSyntaxHighlighter</a> 的子类来实现语法高亮。</p>\r\n<p style=\"text-align: center;\"><img class=\"aligncenter\" src=\"http://qt-project.org/doc/qt-4.8/images/syntaxhighlighter-example.png\" alt=\"语法高亮\" width=\"635\" height=\"412\" /></p>\r\n<p>上图是一个C++文件的语法高亮显示</p>\r\n<p>该实例中包含两个类：</p>\r\n<ul>\r\n<li>Highlighter 类定义和实现高亮规则</li>\r\n<li>MainWindow 组件是应用程序的主窗口</li>\r\n</ul>\r\n<p>为实现我们自己的语法高亮，我们必须子类化 <a href=\"http://qt-project.org/doc/qt-4.8/qsyntaxhighlighter.html\">QSyntaxHighlighter</a>，重新实现其 highlightBlock() 函数，并且定义我们自己的高亮规则。</p>\r\n<p>我们应该把高亮规则存放在一个列表中：一个规则由一个 <a href=\"http://qt-project.org/doc/qt-4.8/qregexp.html\">QRegExp</a> 和一个 <a href=\"http://qt-project.org/doc/qt-4.8/qtextcharformat.html\">QTextCharFormat</a> 实例组成。</p>\r\n<p>Highlighter 类的实现</p>\r\n<p>当子类化 <a href=\"http://qt-project.org/doc/qt-4.8/qsyntaxhighlighter.html\">QSyntaxHighlighter</a>&nbsp;类时你必须把父参数传递给其构造函数。其父参数就是在其上显示高亮语法的 QTextDocument。</p>\r\n<pre class=\"prettyprint\">class Highlighter(QtGui.QSyntaxHighlighter):\r\n    def __init__(self, parent=None):\r\n        super(Highlighter, self).__init__(parent)\r\n\r\n        keywordFormat = QtGui.QTextCharFormat()\r\n        keywordFormat.setForeground(QtCore.Qt.darkBlue)\r\n        keywordFormat.setFontWeight(QtGui.QFont.Bold)\r\n\r\n        keywordPatterns = [r\"\\bchar\\b\", r\"\\bclass\\b\", r\"\\bconst\\b\",\r\n                           r\"\\bdouble\\b\", r\"\\benum\\b\", r\"\\bexplicit\\b\",\r\n                           r\"\\bfriend\\b\", r\"\\binline\\b\", r\"\\bint\\b\",\r\n                           r\"\\blong\\b\", r\"\\bnamespace\\b\", r\"\\boperator\\b\",\r\n                           r\"\\bprivate\\b\", r\"\\bprotected\\b\", r\"\\bpublic\\b\",\r\n                           r\"\\bshort\\b\", r\"\\bsignals\\b\", r\"\\bsigned\\b\",\r\n                           r\"\\bslots\\b\", r\"\\bstatic\\b\", r\"\\bstruct\\b\",\r\n                           r\"\\btemplate\\b\", r\"\\btypedef\\b\", r\"\\btypename\\b\",\r\n                           r\"\\bunion\\b\", r\"\\bunsigned\\b\", r\"\\bvirtual\\b\",\r\n                           r\"\\bvoid\\b\", r\"\\bvolatile\\b\", ]\r\n\r\n        self.highlightingRules = [(QtCore.QRegExp(pattern), keywordFormat)\r\n                for pattern in keywordPatterns]</pre>\r\n<p>首先我们定义一个关键字规则来识别所有的 C++ 关键字。我们为 keywordFormat 设置一个深蓝色的粗体。</p>\r\n<pre class=\"prettyprint\">        classFormat = QtGui.QTextCharFormat()\r\n        classFormat.setFontWeight(QtGui.QFont.Bold)\r\n        classFormat.setForeground(QtCore.Qt.darkMagenta)\r\n        self.highlightingRules.append((QtCore.QRegExp(\"\\\\bQ[A-Za-z]+\\\\b\"),\r\n                classFormat))\r\n\r\n        quotationFormat = QtGui.QTextCharFormat()\r\n        quotationFormat.setForeground(QtCore.Qt.darkGreen)\r\n        self.highlightingRules.append((QtCore.QRegExp(\"\\\".*\\\"\"),\r\n                quotationFormat))\r\n\r\n        functionFormat = QtGui.QTextCharFormat()\r\n        functionFormat.setFontItalic(True)\r\n        functionFormat.setForeground(QtCore.Qt.blue)\r\n        self.highlightingRules.append((QtCore.QRegExp(\"\\\\b[A-Za-z0-9_]+(?=\\\\()\"),\r\n                functionFormat))</pre>\r\n<p>然后我们创建一个应用在类名上的格式。所有的类名都会显示成为深紫色的粗体。我们再指定一个正则表达式来捕获所有的类名。</p>\r\n<p>我们用同样的方法来定义双引号中的内容（字符串）和函数的高亮显示。</p>\r\n<pre class=\"prettyprint\">        singleLineCommentFormat = QtGui.QTextCharFormat()\r\n        singleLineCommentFormat.setForeground(QtCore.Qt.red)\r\n        self.highlightingRules.append((QtCore.QRegExp(\"//[^\\n]*\"),\r\n                singleLineCommentFormat))\r\n\r\n        self.multiLineCommentFormat = QtGui.QTextCharFormat()\r\n        self.multiLineCommentFormat.setForeground(QtCore.Qt.red)\r\n\r\n        self.commentStartExpression = QtCore.QRegExp(\"/\\\\*\")\r\n        self.commentEndExpression = QtCore.QRegExp(\"\\\\*/\")</pre>\r\n<p>C++语言有两种注释方法：单行注释（//）和多行注释（/* ... */）。单行注释可以像前一个一样简单的定义。但是多行注释需要多注意。</p>\r\n<p>在 <a href=\"http://qt-project.org/doc/qt-4.8/qsyntaxhighlighter.html\">QSyntaxHighlighter</a>&nbsp;对象被创建后，它的 <a href=\"http://qt-project.org/doc/qt-4.8/qsyntaxhighlighter.html#highlightBlock\">highlightBlock()</a>&nbsp;函数会在富文本引擎需要的时候被自动调用，高亮显示给定的文本块。</p>\r\n<pre class=\"prettyprint\">    def highlightBlock(self, text):\r\n        for pattern, format in self.highlightingRules:\r\n            expression = QtCore.QRegExp(pattern)\r\n            index = expression.indexIn(text)\r\n            while index &gt;= 0:\r\n                length = expression.matchedLength()\r\n                self.setFormat(index, length, format)\r\n                index = expression.indexIn(text, index + length)</pre>\r\n<p><a href=\"http://qt-project.org/doc/qt-4.8/qsyntaxhighlighter.html#highlightBlock\">highlightBlock()</a>&nbsp;函数会在富文本引擎需要的时候被自动调用，高亮显示给定的文本块。例如在文本块发生改变时。</p>\r\n<p>首先我们应用存储在 highlightingRules 列表中的语法高亮规则。对于每一条规则我们在给定的文本块中搜索符合的 pattern。当找到第一个匹配时，我们使用 <a href=\"http://qt-project.org/doc/qt-4.8/qregexp.html#matchedLength\">QRegExp.matchedLength()</a> 函数来决定将要被格式化的字符串。<a href=\"http://qt-project.org/doc/qt-4.8/qregexp.html#matchedLength\">QRegExp.matchedLength()</a>&nbsp;返回最后一次匹配的字符串的长度，如果返回 -1 表示没有匹配上。</p>\r\n<p>实际的格式化操作是通过 <a href=\"http://qt-project.org/doc/qt-4.8/qsyntaxhighlighter.html\">QSyntaxHighlighter</a>&nbsp;类的 <a href=\"http://qt-project.org/doc/qt-4.8/qsyntaxhighlighter.html#setFormat\">setFormat()</a>&nbsp;函数实现的。该函数操纵作为 <a href=\"http://qt-project.org/doc/qt-4.8/qsyntaxhighlighter.html#highlightBlock\">highlightBlock()</a>&nbsp;函数参数传递进来的文本块。指定的 format 会从开始位置到及给定的长度来格式化文本。注意文档本身还是保持在没有被修改的状态。该项操作要一直进行直到最后一个匹配的为止。</p>\r\n<pre class=\"prettyprint\">        self.setCurrentBlockState(0)</pre>\r\n<p>在处理跨多个文本块（例如：多行注释），需要知道上一个文本块的结束状态（如，在注释中）。在 highlightBlock() 的实现中你可通过 <a href=\"http://qt-project.org/doc/qt-4.8/qsyntaxhighlighter.html#previousBlockState\">QSyntaxHighlighter.previousBlockState()</a> 函数来得到上一个文本块的最后状态。在解析完文本块后可以通过使用 <a href=\"http://qt-project.org/doc/qt-4.8/qsyntaxhighlighter.html#setCurrentBlockState\">QSyntaxHighlighter.setCurrentBlockState()</a> 函数在设置最后状态。</p>\r\n<p><a href=\"http://qt-project.org/doc/qt-4.8/qsyntaxhighlighter.html#previousBlockState\">QSyntaxHighlighter.previousBlockState()</a>&nbsp;函数返回一个整数。如果没有设置状态，则返回 -1。你可以用<a href=\"http://qt-project.org/doc/qt-4.8/qsyntaxhighlighter.html#setCurrentBlockState\">QSyntaxHighlighter.setCurrentBlockState()</a>&nbsp;函数来标志任何一个状态。一旦状态被设置，QTextBlock 会保持这个值直到它被再次设置或当前文本被删除。</p>\r\n<p>在该实例中我们使用0来表示&ldquo;不在注释中&rdquo;状态，使用 1 表示&ldquo;在注释中&rdquo;状态。当被存储起来的高亮规则被应用时我们初始化当前文本块的状态为 0。</p>\r\n<pre class=\"prettyprint\">        startIndex = 0\r\n        if self.previousBlockState() != 1:\r\n            startIndex = self.commentStartExpression.indexIn(text)</pre>\r\n<p>如果上一个状态为&ldquo;在注释中&rdquo;（previousBlockState() == 1），我们就开始搜索结束表达式。</p>\r\n<pre class=\"prettyprint\">        while startIndex &gt;= 0:\r\n            endIndex = self.commentEndExpression.indexIn(text, startIndex)\r\n\r\n            if endIndex == -1:\r\n                self.setCurrentBlockState(1)\r\n                commentLength = len(text) - startIndex\r\n            else:\r\n                commentLength = endIndex - startIndex + \\\r\n                    self.commentEndExpression.matchedLength()\r\n\r\n            self.setFormat(startIndex, commentLength,\r\n                    self.multiLineCommentFormat)\r\n            startIndex = self.commentStartExpression.indexIn(text,\r\n                    startIndex + commentLength);</pre>\r\n<p>如果结束表达式被找到，我们计算注释的长度并且应用注释的格式。然后开始搜索下一个开始表达式并重复该过程。如果没有任何结束表达式在当前文本块中被找到，我们设置当前文本块的状态为1（&ldquo;在注释中&rdquo;）</p>\r\n<p>以下是源代码：</p>\r\n<pre class=\"prettyprint\">\"\"\"PyQt4 port of the richtext/syntaxhighlighter example from Qt v4.x\"\"\"\r\n\r\nfrom PyQt4 import QtCore, QtGui\r\n\r\nclass MainWindow(QtGui.QMainWindow):\r\n    def __init__(self, parent=None):\r\n        super(MainWindow, self).__init__(parent)\r\n\r\n        self.setupFileMenu()\r\n        self.setupHelpMenu()\r\n        self.setupEditor()\r\n\r\n        self.setCentralWidget(self.editor)\r\n        self.setWindowTitle(\"Syntax Highlighter\")\r\n\r\n    def about(self):\r\n        QtGui.QMessageBox.about(self, \"About Syntax Highlighter\",\r\n                \"&lt;p&gt;The &lt;b&gt;Syntax Highlighter&lt;/b&gt; example shows how to \" \\\r\n                \"perform simple syntax highlighting by subclassing the \" \\\r\n                \"QSyntaxHighlighter class and describing highlighting \" \\\r\n                \"rules using regular expressions.&lt;/p&gt;\")\r\n\r\n    def newFile(self):\r\n        self.editor.clear()\r\n\r\n    def openFile(self, path=None):\r\n        if not path:\r\n            path = QtGui.QFileDialog.getOpenFileName(self, \"Open File\",\r\n                    \'\', \"C++ Files (*.cpp *.h)\")\r\n\r\n        if path:\r\n            inFile = QtCore.QFile(path)\r\n            if inFile.open(QtCore.QFile.ReadOnly | QtCore.QFile.Text):\r\n                text = inFile.readAll()\r\n                try:\r\n                    # Python v3.\r\n                    text = str(text, encoding=\'ascii\')\r\n                except TypeError:\r\n                    # Python v2.\r\n                    text = str(text)\r\n                self.editor.setPlainText(text)\r\n\r\n    def setupEditor(self):\r\n        font = QtGui.QFont()\r\n        font.setFamily(\'Courier\')\r\n        font.setFixedPitch(True)\r\n        font.setPointSize(10)\r\n\r\n        self.editor = QtGui.QTextEdit()\r\n        self.editor.setFont(font)\r\n\r\n        self.highlighter = Highlighter(self.editor.document())\r\n\r\n    def setupFileMenu(self):\r\n        fileMenu = QtGui.QMenu(\"&amp;File\", self)\r\n        self.menuBar().addMenu(fileMenu)\r\n\r\n        fileMenu.addAction(\"&amp;New...\", self.newFile, \"Ctrl+N\")\r\n        fileMenu.addAction(\"&amp;Open...\", self.openFile, \"Ctrl+O\")\r\n        fileMenu.addAction(\"E&amp;xit\", QtGui.qApp.quit, \"Ctrl+Q\")\r\n\r\n    def setupHelpMenu(self):\r\n        helpMenu = QtGui.QMenu(\"&amp;Help\", self)\r\n        self.menuBar().addMenu(helpMenu)\r\n\r\n        helpMenu.addAction(\"&amp;About\", self.about)\r\n        helpMenu.addAction(\"About &amp;Qt\", QtGui.qApp.aboutQt)\r\n\r\n\r\nclass Highlighter(QtGui.QSyntaxHighlighter):\r\n    def __init__(self, parent=None):\r\n        super(Highlighter, self).__init__(parent)\r\n\r\n        keywordFormat = QtGui.QTextCharFormat()\r\n        keywordFormat.setForeground(QtCore.Qt.darkBlue)\r\n        keywordFormat.setFontWeight(QtGui.QFont.Bold)\r\n\r\n        keywordPatterns = [r\"\\bchar\\b\", r\"\\bclass\\b\", r\"\\bconst\\b\",\r\n                           r\"\\bdouble\\b\", r\"\\benum\\b\", r\"\\bexplicit\\b\",\r\n                           r\"\\bfriend\\b\", r\"\\binline\\b\", r\"\\bint\\b\",\r\n                           r\"\\blong\\b\", r\"\\bnamespace\\b\", r\"\\boperator\\b\",\r\n                           r\"\\bprivate\\b\", r\"\\bprotected\\b\", r\"\\bpublic\\b\",\r\n                           r\"\\bshort\\b\", r\"\\bsignals\\b\", r\"\\bsigned\\b\",\r\n                           r\"\\bslots\\b\", r\"\\bstatic\\b\", r\"\\bstruct\\b\",\r\n                           r\"\\btemplate\\b\", r\"\\btypedef\\b\", r\"\\btypename\\b\",\r\n                           r\"\\bunion\\b\", r\"\\bunsigned\\b\", r\"\\bvirtual\\b\",\r\n                           r\"\\bvoid\\b\", r\"\\bvolatile\\b\", ]\r\n\r\n        self.highlightingRules = [(QtCore.QRegExp(pattern), keywordFormat)\r\n                for pattern in keywordPatterns]\r\n\r\n        classFormat = QtGui.QTextCharFormat()\r\n        classFormat.setFontWeight(QtGui.QFont.Bold)\r\n        classFormat.setForeground(QtCore.Qt.darkMagenta)\r\n        self.highlightingRules.append((QtCore.QRegExp(\"\\\\bQ[A-Za-z]+\\\\b\"),\r\n                classFormat))\r\n\r\n        quotationFormat = QtGui.QTextCharFormat()\r\n        quotationFormat.setForeground(QtCore.Qt.darkGreen)\r\n        self.highlightingRules.append((QtCore.QRegExp(\"\\\".*\\\"\"),\r\n                quotationFormat))\r\n\r\n        functionFormat = QtGui.QTextCharFormat()\r\n        functionFormat.setFontItalic(True)\r\n        functionFormat.setForeground(QtCore.Qt.blue)\r\n        self.highlightingRules.append((QtCore.QRegExp(\"\\\\b[A-Za-z0-9_]+(?=\\\\()\"),\r\n                functionFormat))\r\n\r\n        singleLineCommentFormat = QtGui.QTextCharFormat()\r\n        singleLineCommentFormat.setForeground(QtCore.Qt.red)\r\n        self.highlightingRules.append((QtCore.QRegExp(\"//[^\\n]*\"),\r\n                singleLineCommentFormat))\r\n\r\n        self.multiLineCommentFormat = QtGui.QTextCharFormat()\r\n        self.multiLineCommentFormat.setForeground(QtCore.Qt.red)\r\n\r\n        self.commentStartExpression = QtCore.QRegExp(\"/\\\\*\")\r\n        self.commentEndExpression = QtCore.QRegExp(\"\\\\*/\")\r\n\r\n    def highlightBlock(self, text):\r\n        for pattern, format in self.highlightingRules:\r\n            expression = QtCore.QRegExp(pattern)\r\n            index = expression.indexIn(text)\r\n            while index &gt;= 0:\r\n                length = expression.matchedLength()\r\n                self.setFormat(index, length, format)\r\n                index = expression.indexIn(text, index + length)\r\n\r\n        self.setCurrentBlockState(0)\r\n\r\n        startIndex = 0\r\n        if self.previousBlockState() != 1:\r\n            startIndex = self.commentStartExpression.indexIn(text)\r\n\r\n        while startIndex &gt;= 0:\r\n            endIndex = self.commentEndExpression.indexIn(text, startIndex)\r\n\r\n            if endIndex == -1:\r\n                self.setCurrentBlockState(1)\r\n                commentLength = len(text) - startIndex\r\n            else:\r\n                commentLength = endIndex - startIndex + \\\r\n                    self.commentEndExpression.matchedLength()\r\n\r\n            self.setFormat(startIndex, commentLength,\r\n                    self.multiLineCommentFormat)\r\n            startIndex = self.commentStartExpression.indexIn(text,\r\n                    startIndex + commentLength);\r\n\r\nif __name__ == \'__main__\':\r\n\r\n    import sys\r\n\r\n    app = QtGui.QApplication(sys.argv)\r\n    window = MainWindow()\r\n    window.resize(640, 512)\r\n    window.show()\r\n    sys.exit(app.exec_())</pre>','',1,1,'2012-07-01 12:13:25','2012-07-01 12:13:25',19,'python, pyqt',NULL);
/*!40000 ALTER TABLE `blog_article` ENABLE KEYS */;
UNLOCK TABLES;

--
-- Table structure for table `blog_userprofile`
--

DROP TABLE IF EXISTS `blog_userprofile`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `blog_userprofile` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `nickname` varchar(30) DEFAULT NULL,
  `image_url` varchar(256) DEFAULT NULL,
  `gender` int(11) DEFAULT NULL,
  `telephone` varchar(20) DEFAULT NULL,
  `qq` varchar(20) DEFAULT NULL,
  `msn` varchar(30) DEFAULT NULL,
  `birthday` date DEFAULT NULL,
  `birth_town` varchar(30) DEFAULT NULL,
  `live_town` varchar(30) DEFAULT NULL,
  `description` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_id` (`user_id`),
  CONSTRAINT `user_id_refs_id_3b63a6f3` FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;

--
-- Dumping data for table `blog_userprofile`
--

LOCK TABLES `blog_userprofile` WRITE;
/*!40000 ALTER TABLE `blog_userprofile` DISABLE KEYS */;
INSERT INTO `blog_userprofile` VALUES (2,1,'Senbonzakura','/ywjsailor/portrait.jpeg',1,'','','senbonzakura@hotmail.co.jp',NULL,'','','');
/*!40000 ALTER TABLE `blog_userprofile` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2012-08-25 12:19:31
